<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 7.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.ico">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha256-wiz7ZSCn/btzhjKDQBms9Hx4sSeUYsDrTLg7roPstac=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.css" integrity="sha256-6cQIC71/iBIYXFK+0RHAvwmjwWzkWd+r7v/BX3/vZDc=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/themes/green/pace-theme-minimal.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/pace.min.js" integrity="sha256-gqd7YTjg/BtfqWSwsJOvndl0Bxc8gFImLEkXQT8+qj0=" crossorigin="anonymous"></script>

<script class="next-config" data-name="main" type="application/json">{"hostname":"sumumm.github.io","root":"/","images":"/images","scheme":"Gemini","darkmode":false,"version":"8.19.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":true,"style":"mac"},"fold":{"enable":true,"height":300},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":true,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":true,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta name="description" content="来详细了解下I2C的驱动框架。若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:type" content="article">
<meta property="og:title" content="LV08-01-I2C子系统-04-I2C驱动框架分析">
<meta property="og:url" content="https://sumumm.github.io/post/2d9f95cb.html">
<meta property="og:site_name" content="苏木">
<meta property="og:description" content="来详细了解下I2C的驱动框架。若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250328092854901.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330141349194.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330141544900.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330141610365.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329162411698.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329162949427.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330135916716.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330140012476.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330140511185.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330173825291.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330173931777.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330174321446.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330174422138.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250328113706039.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329105907331.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329110046602.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329110358912.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329110637057.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329110910312.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250328113804113.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/072_master_xfer_imx6ull.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330145705461.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330163641600.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330164220088.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330164312223.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330164613670.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330164711276.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/009_i2c_signal.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329131418887.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329140210360.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329135722971.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329140631954.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330170557596.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330170813059.png">
<meta property="article:published_time" content="2025-04-07T23:38:03.000Z">
<meta property="article:modified_time" content="2025-06-13T16:25:57.058Z">
<meta property="article:author" content="苏木">
<meta property="article:tag" content="LV08-I2C子系统">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250328092854901.png">


<link rel="canonical" href="https://sumumm.github.io/post/2d9f95cb.html">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":"","permalink":"https://sumumm.github.io/post/2d9f95cb.html","path":"post/2d9f95cb.html","title":"LV08-01-I2C子系统-04-I2C驱动框架分析"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>LV08-01-I2C子系统-04-I2C驱动框架分析 | 苏木</title>
  








    <script src="/js/browser_tools_disable.js"></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
<!-- hexo injector head_end start --><link rel="stylesheet" href="https://unpkg.com/hexo-next-tags-plus@latest/lib/tag_plus.css" media="defer" onload="this.media='all'"><!-- hexo injector head_end end --></head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">苏木</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">我的学习之路</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>苏木的家</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类页<span class="badge">42</span></a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档页<span class="badge">673</span></a></li><li class="menu-item menu-item-flink"><a href="/flink/" rel="section"><i class="fa fa-link fa-fw"></i>友人帐</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于我</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81I2C%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8%E5%88%86%E6%9E%90"><span class="nav-text">一、I2C设备驱动分析</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E6%B3%A8%E5%86%8Ci2c-driver"><span class="nav-text">1. 注册i2c_driver</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-%E6%B3%A8%E5%86%8CI2C%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8"><span class="nav-text">1.1 注册I2C设备驱动</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-%E9%94%80%E6%AF%81I2C%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8"><span class="nav-text">1.2 销毁I2C设备驱动</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-i2c-driver%E7%BB%93%E6%9E%84%E4%BD%93%E6%B3%A8%E5%86%8C%E7%A4%BA%E4%BE%8B"><span class="nav-text">1.3 i2c_driver结构体注册示例</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-4-i2c-driver%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6"><span class="nav-text">1.4 i2c_driver驱动框架</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-5-%E5%9C%A8sysfs%E4%B8%AD%E7%9A%84%E4%BD%93%E7%8E%B0"><span class="nav-text">1.5 在sysfs中的体现</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E6%B3%A8%E5%86%8Ci2c-client"><span class="nav-text">2. 注册i2c_client</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-%E6%96%B9%E5%BC%8F%E4%B8%80"><span class="nav-text">2.1 方式一</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-1-%E9%80%9A%E8%BF%87devicetree%E5%A3%B0%E6%98%8EI2C%E8%AE%BE%E5%A4%87"><span class="nav-text">2.1.1 通过devicetree声明I2C设备</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-2-%E6%A0%B9%E6%8D%AE%E6%80%BB%E7%BA%BF%E5%8F%B7%E5%A3%B0%E6%98%8EI2C%E8%AE%BE%E5%A4%87"><span class="nav-text">2.1.2 根据总线号声明I2C设备</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-%E6%96%B9%E5%BC%8F%E4%BA%8C%EF%BC%9A%E6%98%BE%E5%BC%8F%E5%9C%B0%E5%AE%9E%E4%BE%8B%E5%8C%96%E8%AE%BE%E5%A4%87"><span class="nav-text">2.2 方式二：显式地实例化设备</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#2-2-1-i2c-new-device"><span class="nav-text">2.2.1 i2c_new_device()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-2-2-i2c-new-probed-device"><span class="nav-text">2.2.2 i2c_new_probed_device()</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-%E6%96%B9%E5%BC%8F%E4%B8%89%EF%BC%9A%E6%8E%A2%E6%B5%8B%E7%89%B9%E5%AE%9A%E8%AE%BE%E5%A4%87%E7%9A%84I2C%E6%80%BB%E7%BA%BF"><span class="nav-text">2.3 方式三：探测特定设备的I2C总线</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-4-%E6%96%B9%E5%BC%8F%E5%9B%9B%EF%BC%9A%E4%BB%8E%E7%94%A8%E6%88%B7%E7%A9%BA%E9%97%B4%E5%AE%9E%E4%BE%8B%E5%8C%96"><span class="nav-text">2.4 方式四：从用户空间实例化</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-5-i2c-client%E8%AE%BE%E5%A4%87%E6%B3%A8%E5%86%8C%E7%A4%BA%E4%BE%8B"><span class="nav-text">2.5 i2c_client设备注册示例</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-6-%E5%9C%A8sysfs%E4%B8%AD%E7%9A%84%E4%BD%93%E7%8E%B0"><span class="nav-text">2.6 在sysfs中的体现</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-I2C%E8%AE%BE%E5%A4%87%E5%92%8C%E9%A9%B1%E5%8A%A8%E7%9A%84%E5%8C%B9%E9%85%8D"><span class="nav-text">3. I2C设备和驱动的匹配</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-%E8%AE%BE%E5%A4%87%E6%A0%91%E5%8C%B9%E9%85%8D%EF%BC%9Ai2c-of-match-device"><span class="nav-text">3.1 设备树匹配：i2c_of_match_device()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-id%E5%8C%B9%E9%85%8D%EF%BC%9Ai2c-match-id"><span class="nav-text">3.2 id匹配：i2c_match_id()</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-I2C%E8%AE%BE%E5%A4%87%E6%94%B6%E5%8F%91%E6%95%B0%E6%8D%AE"><span class="nav-text">4. I2C设备收发数据</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-i2c-transfer"><span class="nav-text">4.1 i2c_transfer()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-%E6%94%B6%E5%8F%91%E7%A4%BA%E4%BE%8B"><span class="nav-text">4.2 收发示例</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-1-%E8%AE%BE%E5%A4%87%E7%BB%93%E6%9E%84%E4%BD%93"><span class="nav-text">4.2.1 设备结构体</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-2-xxx-read-regs"><span class="nav-text">4.2.2 xxx_read_regs()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-3-xxx-write-regs"><span class="nav-text">4.2.3 xxx_write_regs()</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-ap3216c%E5%AE%9E%E4%BE%8B"><span class="nav-text">5. ap3216c实例</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-1-demo%E6%BA%90%E7%A0%81"><span class="nav-text">5.1 demo源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-%E5%BC%80%E5%8F%91%E6%9D%BF%E6%B5%8B%E8%AF%95"><span class="nav-text">5.2 开发板测试</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81I2C%E6%80%BB%E7%BA%BF%E9%A9%B1%E5%8A%A8%E5%88%86%E6%9E%90"><span class="nav-text">二、I2C总线驱动分析</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-I-MX6U-I2C-%E7%AE%80%E4%BB%8B"><span class="nav-text">1. I.MX6U I2C 简介  </span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-I2Cx-IADR-x-1-4"><span class="nav-text">1.1 I2Cx_IADR(x&#x3D;1~4)  </span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-I2Cx-IFDR-x-1-4"><span class="nav-text">1.2 I2Cx_IFDR  (x&#x3D;1~4)  </span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-I2Cx-I2CR-x-1-4"><span class="nav-text">1.3 I2Cx_I2CR    (x&#x3D;1~4)  </span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-4-I2Cx-I2SR-x-1-4"><span class="nav-text">1.4 I2Cx_I2SR   (x&#x3D;1~4)  </span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-5-I2Cx-I2DR-x-1-4"><span class="nav-text">1.5 I2Cx_I2DR     (x&#x3D;1~4)  </span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-I2C1-%E6%8E%A7%E5%88%B6%E5%99%A8%E8%8A%82%E7%82%B9"><span class="nav-text">2.  I2C1 控制器节点</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-I2C%E9%80%82%E9%85%8D%E5%99%A8%E9%A9%B1%E5%8A%A8"><span class="nav-text">3. I2C适配器驱动</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-i2c-imx-driver-of-match-table"><span class="nav-text">3.1 i2c_imx_driver.of_match_table</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-i2c-imx-driver-i2c-imx-probe"><span class="nav-text">3.2 i2c_imx_driver. i2c_imx_probe</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3-i2c-imx-algo"><span class="nav-text">3.3 i2c_imx_algo</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-4-i2c-adapter%E6%B3%A8%E5%86%8C-%E6%B3%A8%E9%94%80%E5%87%BD%E6%95%B0"><span class="nav-text">3.4 i2c_adapter注册&#x2F;注销函数</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#3-4-1-i2c-add-adapter"><span class="nav-text">3.4.1 i2c_add_adapter()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-4-2-i2c-add-numbered-adapter"><span class="nav-text">3.4.2 i2c_add_numbered_adapter()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-4-3-i2c-del-adapter"><span class="nav-text">3.4.3 i2c_del_adapter()</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-%E7%BC%96%E5%86%99%E4%B8%80%E4%B8%AA%E8%99%9A%E6%8B%9F%E7%9A%84i2c-adapter"><span class="nav-text">4. 编写一个虚拟的i2c_adapter</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-%E8%AE%BE%E5%A4%87%E6%A0%91"><span class="nav-text">4.1 设备树</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-%E9%80%82%E9%85%8D%E5%99%A8%E7%9A%84%E9%A9%B1%E5%8A%A8"><span class="nav-text">4.2 适配器的驱动</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-1-%E8%A6%81%E5%81%9A%E5%93%AA%E4%BA%9B%EF%BC%9F"><span class="nav-text">4.2.1 要做哪些？</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-2-i2c-adapter%E6%A1%86%E6%9E%B6"><span class="nav-text">4.2.2 i2c_adapter框架</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-3-%E6%A8%A1%E6%8B%9FEEPROM"><span class="nav-text">4.2.3 模拟EEPROM</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-3-%E6%A8%A1%E6%8B%9FEEPROM-demo"><span class="nav-text">4.3 模拟EEPROM demo</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#4-3-1-demo%E6%BA%90%E7%A0%81"><span class="nav-text">4.3.1 demo源码</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-3-2-%E6%B5%8B%E8%AF%95%E7%BB%93%E6%9E%9C"><span class="nav-text">4.3.2 测试结果</span></a></li></ol></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E3%80%81GPIO%E6%A8%A1%E6%8B%9FI2C"><span class="nav-text">三、GPIO模拟I2C</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-GPIO%E6%A8%A1%E6%8B%9FI2C%E7%AE%80%E4%BB%8B"><span class="nav-text">1. GPIO模拟I2C简介</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-GPIO%E5%BC%95%E8%84%9A%E5%AE%9A%E4%B9%89"><span class="nav-text">2. GPIO引脚定义</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-i2c-gpio%E5%9F%BA%E6%9C%AC%E6%A1%86%E6%9E%B6"><span class="nav-text">3. i2c-gpio基本框架</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-%E5%A6%82%E4%BD%95%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%A8%A1%E6%8B%9FI2C%EF%BC%9F"><span class="nav-text">4. 如何添加一个模拟I2C？</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-%E9%A9%B1%E5%8A%A8%E7%9B%B8%E5%85%B3%E6%96%87%E4%BB%B6%E6%94%BE%E5%9C%A8%E9%82%A3%E9%87%8C%EF%BC%9F"><span class="nav-text">4.1 驱动相关文件放在那里？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-%E4%BD%BF%E8%83%BD%E5%86%85%E6%A0%B8%E7%9A%84I2C-GPIO%E9%A9%B1%E5%8A%A8"><span class="nav-text">4.2 使能内核的I2C GPIO驱动</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-3-%E8%AE%BE%E5%A4%87%E6%A0%91%E9%85%8D%E7%BD%AE"><span class="nav-text">4.3 设备树配置</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#4-3-1-%E6%A8%A1%E6%8B%9FI2C%E8%8A%82%E7%82%B9"><span class="nav-text">4.3.1 模拟I2C节点</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-3-2-%E4%BF%AE%E6%94%B9aliases"><span class="nav-text">4.3.2 修改aliases</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-3-3-open-drain%E5%B1%9E%E6%80%A7"><span class="nav-text">4.3.3 open drain属性</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-3-4-%E6%B7%BB%E5%8A%A0i2c%E8%AE%BE%E5%A4%87"><span class="nav-text">4.3.4 添加i2c设备</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-3-5-%E5%BC%80%E5%8F%91%E6%9D%BF%E6%B5%8B%E8%AF%95"><span class="nav-text">4.3.5 开发板测试</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-i2c-gpio%E9%A9%B1%E5%8A%A8%E5%88%86%E6%9E%90"><span class="nav-text">5. i2c-gpio驱动分析</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-1-i2c-gpio-driver-driver-of-match-table"><span class="nav-text">5.1 i2c_gpio_driver.driver.of_match_table</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-i2c-gpio-driver-probe"><span class="nav-text">5.2 i2c_gpio_driver.probe</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#5-2-1-%E8%A7%A3%E6%9E%90%E8%AE%BE%E5%A4%87%E6%A0%91"><span class="nav-text">5.2.1 解析设备树</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#5-2-2-%E6%B3%A8%E5%86%8C%E5%88%B0i2c-algo-bit-c"><span class="nav-text">5.2.2 注册到i2c-algo-bit.c</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-3-i2c-bit-algo"><span class="nav-text">5.3 i2c_bit_algo</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#5-3-1-i2c-bit-algo-functionality"><span class="nav-text">5.3.1 i2c_bit_algo.functionality</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#5-3-2-i2c-bit-algo-master-xfer"><span class="nav-text">5.3.2 i2c_bit_algo.master_xfer</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-GPIO%E6%A8%A1%E6%8B%9FI2Cdemo"><span class="nav-text">6. GPIO模拟I2Cdemo</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#6-1-demo%E6%BA%90%E7%A0%81"><span class="nav-text">6.1 demo源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#6-2-%E5%BC%80%E5%8F%91%E6%9D%BF%E6%B5%8B%E8%AF%95"><span class="nav-text">6.2 开发板测试</span></a></li></ol></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="苏木"
      src="/images/avatar.jpg">
  <p class="site-author-name" itemprop="name">苏木</p>
  <div class="site-description" itemprop="description">莫道桑榆晚，为霞尚满天</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">673</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">42</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">43</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author animated">
      <span class="links-of-author-item">
        <a href="https://github.com/sumumm" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;sumumm" rel="noopener me" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
  </div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://sumumm.github.io/post/2d9f95cb.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.jpg">
      <meta itemprop="name" content="苏木">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="苏木">
      <meta itemprop="description" content="莫道桑榆晚，为霞尚满天">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="LV08-01-I2C子系统-04-I2C驱动框架分析 | 苏木">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          LV08-01-I2C子系统-04-I2C驱动框架分析
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2025-04-08 07:38:03" itemprop="dateCreated datePublished" datetime="2025-04-08T07:38:03+08:00">2025-04-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/" itemprop="url" rel="index"><span itemprop="name">嵌入式开发</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/" itemprop="url" rel="index"><span itemprop="name">02IMX6ULL平台</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/" itemprop="url" rel="index"><span itemprop="name">LV08-I2C子系统</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>14k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>52 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody"><p>来详细了解下I2C的驱动框架。若笔记中有错误或者不合适的地方，欢迎批评指正😃。</p>
<span id="more"></span>

<!-- Photo: https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/ -->

<details class="folding-tag" blue><summary> 点击查看使用工具及版本 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center" rowspan="5">PC端开发环境</td>        <td align="center" width=150px>Windows</td>        <td align="left">Windows11</td>    </tr>    <tr>        <td align="center">Ubuntu</td>        <td align="left">Ubuntu20.04.2的64位版本</td>      </tr>    <tr>        <td align="center">VMware® Workstation 17 Pro</td>        <td align="left">17.6.0 build-24238078</td>      </tr>    <tr>        <td align="center">终端软件</td>        <td align="left">MobaXterm(Professional Edition v23.0 Build 5042 (license))</td>    </tr>    <tr>        <td align="center">Win32DiskImager</td>        <td align="left">Win32DiskImager v1.0</td>      </tr>    <tr>        <td align="center" rowspan="3">Linux开发板环境</td>        <td align="center">Linux开发板</td>        <td align="left">正点原子 i.MX6ULL Linux 阿尔法开发板</td>      </tr>    <tr>        <td align="center">uboot</td>        <td align="left">NXP官方提供的uboot，使用的uboot版本为U-Boot 2019.04</td>      </tr>    <tr>        <td align="center">linux内核</td>        <td align="left">linux-4.19.71(NXP官方提供)</td>      </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看本文参考资料 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">分类</td>        <td align="center">网址</td>        <td align="center">说明</td>    </tr>    <tr>        <td align="center" rowspan="5">官方网站</td>        <td align="left"><a href="https://www.arm.com/" target="_blank">https://www.arm.com/</a></td>        <td align="left">ARM官方网站，在这里我们可以找到Cotex-Mx以及ARMVx的一些文档</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxp.com.cn/" target="_blank">https://www.nxp.com.cn/ </a></td>        <td align="left">NXP官方网站</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxpic.org.cn/" target="_blank">https://www.nxpic.org.cn/</a></td><td align="left">NXP 官方社区</td>    </tr>    <tr>        <td align="left"><a href="https://u-boot.readthedocs.io/en/latest/" target="_blank">https://u-boot.readthedocs.io/en/latest/</a></td><td align="left">u-boot官网</td>    </tr>    <tr>        <td align="left"><a href="https://www.kernel.org/" target="_blank">https://www.kernel.org/</a></td><td align="left">linux内核官网</td>    </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看相关文件下载 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">分类</td>        <td align="center">网址</td>        <td align="center">说明</td>    </tr>    <tr>        <td align="center" rowspan="3">NXP</td>        <td align="left"><a href="https://github.com/nxp-imx" target="_blank">https://github.com/nxp-imx</a></td>        <td align="left">NXP imx开发资源GitHub组织，里边会有u-boot和linux内核的仓库</td>    </tr>    <tr>        <td align="left"><a href="https://github.com/nxp-imx/linux-imx/releases/tag/v4.19.71" target="_blank">nxp-imx/linux-imx/releases/tag/v4.19.71</a></td>        <td align="left">NXP linux内核仓库tags中的v4.19.71</td>    </tr>    <tr>        <td align="left"><a href="https://github.com/nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0" target="_blank">nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0</a></td>        <td align="left">NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0</td>    </tr>    <tr>        <td align="center" rowspan="2">I.MX6ULL</td>        <td align="left"><a href="https://www.nxp.com.cn/docs/en/data-sheet/IMX6ULLIEC.pdf" target="_blank">i.MX 6ULL Applications Processors for Industrial Products</a></td>        <td align="left">I.MX6ULL 芯片手册（datasheet，可以在线查看）</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh" target="_blank">i.MX 6ULL Applications ProcessorReference Manual</a></td>        <td align="left">I.MX6ULL 参考手册（下载后才能查看，需要登录NXP官网）</td>    </tr>    <tr>        <td align="center" rowspan="3">Source Code</td>        <td align="left"><a href="https://elixir.bootlin.com/linux/latest/source" target="_blank">https://elixir.bootlin.com/linux/latest/source</a></td>        <td align="left">linux kernel源码</td>    </tr>    <tr>        <td align="left"><a href="https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/?h=v4.19.71&id=e7d2672c66e4d3675570369bf20856296da312c4" target="_blank">kernel/git/stable/linux.git - Linux kernel stable tree</a></td>        <td align="left">linux kernel源码(官网,tag 4.19.71)</td>    </tr>    <tr>        <td align="left"><a href="https://elixir.bootlin.com/u-boot/latest/source" target="_blank">https://elixir.bootlin.com/u-boot/latest/source</a></td>        <td align="left">uboot源码</td>    </tr></table>
              </div>
            </details>

<h1 id="一、I2C设备驱动分析"><a href="#一、I2C设备驱动分析" class="headerlink" title="一、I2C设备驱动分析"></a><font size=3>一、I2C设备驱动分析</font></h1><h2 id="1-注册i2c-driver"><a href="#1-注册i2c-driver" class="headerlink" title="1. 注册i2c_driver"></a><font size=3>1. 注册<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L231">i2c_driver</a></font></h2><h3 id="1-1-注册I2C设备驱动"><a href="#1-1-注册I2C设备驱动" class="headerlink" title="1.1 注册I2C设备驱动"></a><font size=3>1.1 注册I2C设备驱动</font></h3><p>对于我们 I2C 设备驱动编写人来说，重点工作就是创建 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L231">i2c_driver</a>，创建完成以后需要向Linux 内核注册这个 i2c_driver。i2c_driver 注册函数为 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1605">i2c_register_driver()</a> ：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">i2c_register_driver</span><span class="params">(<span class="keyword">struct</span> module *owner, <span class="keyword">struct</span> i2c_driver *driver)</span>;</span><br></pre></td></tr></table></figure>

<p>其中owner 一般为 THIS_MODULE，driver是要注册的 i2c_driver。返回值： 0，成功；负值，失败。</p>
<p>另外 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L804">i2c_add_driver()</a> 也常常用于注册 i2c_driver， <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L804">i2c_add_driver()</a> 是一个宏，定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* use a define to avoid include chaining to get THIS_MODULE */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> i2c_add_driver(driver) \</span></span><br><span class="line"><span class="meta">	i2c_register_driver(THIS_MODULE, driver)</span></span><br></pre></td></tr></table></figure>

<p>其实i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装，只有一个参数，就是要注册的 i2c_driver。  </p>
<h3 id="1-2-销毁I2C设备驱动"><a href="#1-2-销毁I2C设备驱动" class="headerlink" title="1.2 销毁I2C设备驱动"></a><font size=3>1.2 销毁I2C设备驱动</font></h3><p>注销 I2C 设备驱动的时候需要将前面注册的 i2c_driver 从 Linux 内核中注销掉，需要用到<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1641">i2c_del_driver()</a> 函数 :</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">i2c_del_driver</span><span class="params">(<span class="keyword">struct</span> i2c_driver *driver)</span></span><br><span class="line">&#123;</span><br><span class="line">	i2c_for_each_dev(driver, __process_removed_driver);</span><br><span class="line"></span><br><span class="line">	driver_unregister(&amp;driver-&gt;driver);</span><br><span class="line">	pr_debug(<span class="string">&quot;driver [%s] unregistered\n&quot;</span>, driver-&gt;driver.name);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="1-3-i2c-driver结构体注册示例"><a href="#1-3-i2c-driver结构体注册示例" class="headerlink" title="1.3 i2c_driver结构体注册示例"></a><font size=3>1.3 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L231">i2c_driver</a>结构体注册示例</font></h3><p>分配、设置、注册一个i2c_driver结构体：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 传统匹配方式 ID 列表 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_device_id</span> <span class="title">ap3216c_id</span>[] =</span> &#123;</span><br><span class="line">	&#123;<span class="string">&quot;alpha,ap3216c&quot;</span>, <span class="number">0</span>&#125;,</span><br><span class="line">	&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 设备树匹配列表 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">of_device_id</span> <span class="title">ap3216c_of_match</span>[] =</span> &#123;</span><br><span class="line">	&#123; .compatible = <span class="string">&quot;alpha,ap3216c&quot;</span> &#125;,</span><br><span class="line">	&#123; <span class="comment">/* Sentinel */</span> &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* i2c 驱动结构体 */</span></span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_driver</span> <span class="title">ap3216c_driver</span> =</span> &#123;</span><br><span class="line">    .probe = ap3216c_probe,       <span class="comment">// 找到可以支持的设备后，将会调用该函数</span></span><br><span class="line">    .remove = ap3216c_remove,</span><br><span class="line">    .driver = &#123;</span><br><span class="line">        .owner = THIS_MODULE,</span><br><span class="line">        .name = <span class="string">&quot;ap3216c&quot;</span>,</span><br><span class="line">        .of_match_table = ap3216c_of_match, <span class="comment">// 可以支持哪些设备（主要用来判断设备树中的I2C设备）</span></span><br><span class="line">    &#125;,</span><br><span class="line">    .id_table = ap3216c_id,                 <span class="comment">// 可以支持哪些设备，用与id匹配</span></span><br><span class="line">&#125;;  </span><br></pre></td></tr></table></figure>

<p>可以在在ap3216c_probe函数中，分配、设置、注册file_operations结构体。在file_operations的函数中，使用i2c_transfer等函数发起I2C传输。</p>
<h3 id="1-4-i2c-driver驱动框架"><a href="#1-4-i2c-driver驱动框架" class="headerlink" title="1.4 i2c_driver驱动框架"></a><font size=3>1.4 i2c_driver驱动框架</font></h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// （1）第一部分 实现设备的读、写 和初始化等相关功能</span></span><br><span class="line"><span class="comment">//实现i2c总线设备结构体中定义的操作函数，主要是.probe匹配函数，在.probe函数中添加、注册一个字符设备，这个字符设备用于实现i2c设备的具体功能。</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">i2c_write_dev_xxx</span><span class="params">(<span class="keyword">struct</span> i2c_client *mpu6050_client, u8 address, u8 data)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">i2c_read_dev_xxx</span><span class="params">(<span class="keyword">struct</span> i2c_client *mpu6050_client, u8 address, <span class="type">void</span> *data, u32 length)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">dev_xxx_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// （2）第二部分 实现字符设备操作函数集</span></span><br><span class="line"><span class="comment">/*字符设备操作函数集，open函数实现*/</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">dev_xxx_open</span><span class="params">(<span class="keyword">struct</span> inode *inode, <span class="keyword">struct</span> file *filp)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*字符设备操作函数集，.read函数实现*/</span></span><br><span class="line"><span class="type">static</span> <span class="type">ssize_t</span> <span class="title function_">dev_xxx_read</span><span class="params">(<span class="keyword">struct</span> file *filp, <span class="type">char</span> __user *buf, <span class="type">size_t</span> cnt, <span class="type">loff_t</span> *off)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*字符设备操作函数集，.release函数实现*/</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">dev_xxx_release</span><span class="params">(<span class="keyword">struct</span> inode *inode, <span class="keyword">struct</span> file *filp)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*字符设备操作函数集*/</span></span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span> <span class="title">mpu6050_chr_dev_fops</span> =</span></span><br><span class="line">    &#123;</span><br><span class="line">            .owner = THIS_MODULE,</span><br><span class="line">            .open = dev_xxx_open,</span><br><span class="line">            .read = dev_xxx_read,</span><br><span class="line">            .release = dev_xxx_release,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// （3） 第三部分 .probe函数和.remove函数实现</span></span><br><span class="line"><span class="comment">/* i2c 驱动的 probe 函数 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">xxx_probe</span><span class="params">(<span class="keyword">struct</span> i2c_client *client, <span class="type">const</span> <span class="keyword">struct</span> i2c_device_id *id)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">/* 函数具体程序 */</span></span><br><span class="line">    <span class="comment">/* 1、构建设备号 */</span></span><br><span class="line">    <span class="comment">/* 2、注册设备 */</span></span><br><span class="line">    <span class="comment">/* 3、创建类 */</span></span><br><span class="line">    <span class="comment">/* 4、创建设备 */</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* i2c 驱动的 remove 函数 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">xxx_remove</span><span class="params">(<span class="keyword">struct</span> i2c_client *client)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">/* 函数具体程序 */</span></span><br><span class="line">    <span class="comment">/* 删除设备 */</span></span><br><span class="line">    <span class="comment">/* 注销掉类和设备 */</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// （4）第四部分 驱动入口和出口函数实现</span></span><br><span class="line"><span class="comment">/* 传统匹配方式 ID 列表 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_device_id</span> <span class="title">xxx_id</span>[] =</span> &#123;</span><br><span class="line">    &#123;<span class="string">&quot;xxx&quot;</span>, <span class="number">0</span>&#125;,</span><br><span class="line">    &#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 设备树匹配列表 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">of_device_id</span> <span class="title">xxx_of_match</span>[] =</span> &#123;</span><br><span class="line">    &#123; .compatible = <span class="string">&quot;xxx&quot;</span> &#125;,</span><br><span class="line">    &#123; <span class="comment">/* Sentinel */</span> &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* i2c 驱动结构体 */</span></span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_driver</span> <span class="title">xxx_driver</span> =</span> &#123;</span><br><span class="line">    .probe = xxx_probe,</span><br><span class="line">    .remove = xxx_remove,</span><br><span class="line">    .driver = &#123;</span><br><span class="line">        .owner = THIS_MODULE,</span><br><span class="line">        .name = <span class="string">&quot;xxx&quot;</span>,</span><br><span class="line">        .of_match_table = xxx_of_match,</span><br><span class="line">    &#125;,</span><br><span class="line">    .id_table = xxx_id,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 驱动入口函数 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> __init <span class="title function_">xxx_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    ret = i2c_add_driver(&amp;xxx_driver);</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 驱动出口函数 */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> __exit <span class="title function_">xxx_exit</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	i2c_del_driver(&amp;xxx_driver);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">module_init(xxx_init);</span><br><span class="line">module_exit(xxx_exit);</span><br></pre></td></tr></table></figure>

<h3 id="1-5-在sysfs中的体现"><a href="#1-5-在sysfs中的体现" class="headerlink" title="1.5 在sysfs中的体现"></a><font size=3>1.5 在sysfs中的体现</font></h3><p>当我们加载了i2c设备驱动后，可以看一下这个 <code>/sys/bus/i2c/drivers/</code>目录，这个目录下存放着所有的i2c驱动：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250328092854901.png" alt="image-20250328092854901"  />

<h2 id="2-注册i2c-client"><a href="#2-注册i2c-client" class="headerlink" title="2. 注册i2c_client"></a><font size=3>2. 注册<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L309">i2c_client</a></font></h2><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L309">i2c_client</a>表示一个I2C设备，那么我们怎么注册一个 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L309">i2c_client</a>?这部分可以看<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/Documentation/i2c/instantiating-devices">instantiating-devices - Documentation&#x2F;i2c&#x2F;instantiating-devices</a></p>
<h3 id="2-1-方式一"><a href="#2-1-方式一" class="headerlink" title="2.1 方式一"></a><font size=3>2.1 方式一</font></h3><h4 id="2-1-1-通过devicetree声明I2C设备"><a href="#2-1-1-通过devicetree声明I2C设备" class="headerlink" title="2.1.1 通过devicetree声明I2C设备"></a><font size=3>2.1.1 通过devicetree声明I2C设备</font></h4><p>使用设备树的时候 I2C 设备信息通过创建相应的节点就行了，比如 NXP 官方的 EVK 开发板在 I2C1 上接了 mag3110 这个磁力计芯片，因此必须在 i2c1 节点下创建 mag3110 子节点，然后在这个子节点内描述 mag3110 这个芯片的相关信息。我们打开<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi#L136">imx6ul-14x14-evk.dtsi</a>  ，找到以下内容：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&amp;i2c1 &#123;</span><br><span class="line">	clock-frequency = &lt;<span class="number">100000</span>&gt;;</span><br><span class="line">	pinctrl-names = <span class="string">&quot;default&quot;</span>;</span><br><span class="line">	pinctrl<span class="number">-0</span> = &lt;&amp;pinctrl_i2c1&gt;;</span><br><span class="line">	status = <span class="string">&quot;okay&quot;</span>;</span><br><span class="line"></span><br><span class="line">	mag3110@e &#123;</span><br><span class="line">		compatible = <span class="string">&quot;fsl,mag3110&quot;</span>;</span><br><span class="line">		reg = &lt;<span class="number">0x0e</span>&gt;;</span><br><span class="line">	&#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>第 7~11 行，向 i2c1 添加 mag3110 子节点，第 7 行“mag3110@0e”是子节点名字，“@”后面的“0e”就是 mag3110 的 I2C 器件地址。第 8 行设置 compatible 属性值为“fsl,mag3110”。第 9 行的 reg 属性也是设置 mag3110 的器件地址的，因此值为 0x0e。 </p>
<p>I2C 设备节点的<strong>创建重点是 compatible 属性和 reg 属性的设置，一个用于匹配驱动，一个用于设置器件地址。</strong></p>
<p>示例如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">i2c1: i2c@<span class="number">400</span>a0000 &#123;</span><br><span class="line">	<span class="comment">/* ... master properties skipped ... */</span></span><br><span class="line">	clock-frequency = &lt;<span class="number">100000</span>&gt;;</span><br><span class="line"></span><br><span class="line">	flash@<span class="number">50</span> &#123;</span><br><span class="line">		compatible = <span class="string">&quot;atmel,24c256&quot;</span>;</span><br><span class="line">		reg = &lt;<span class="number">0x50</span>&gt;;</span><br><span class="line">	&#125;;</span><br><span class="line"></span><br><span class="line">	pca9532: gpio@<span class="number">60</span> &#123;</span><br><span class="line">		compatible = <span class="string">&quot;nxp,pca9532&quot;</span>;</span><br><span class="line">		gpio-controller;</span><br><span class="line">		<span class="meta">#gpio-cells = <span class="string">&lt;2&gt;</span>;</span></span><br><span class="line">		reg = &lt;<span class="number">0x60</span>&gt;;</span><br><span class="line">	&#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h4 id="2-1-2-根据总线号声明I2C设备"><a href="#2-1-2-根据总线号声明I2C设备" class="headerlink" title="2.1.2 根据总线号声明I2C设备"></a><font size=3>2.1.2 根据总线号声明I2C设备</font></h4><p>首先肯定要描述 I2C 设备节点信息，在未使用设备树的时候需要在 BSP 里面使用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L390">struct i2c_board_info</a> 结构体来描述一个具体的 I2C 设备。   </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">i2c_board_info</span> &#123;</span></span><br><span class="line">	<span class="type">char</span>		type[I2C_NAME_SIZE]; <span class="comment">/* I2C 设备名字 */</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">short</span>	flags; <span class="comment">/* 标志 */</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">short</span>	addr;  <span class="comment">/* I2C 器件地址 */</span></span><br><span class="line">	<span class="type">const</span> <span class="type">char</span>	*dev_name;</span><br><span class="line">	<span class="type">void</span>		*platform_data;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">device_node</span> *<span class="title">of_node</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">fwnode_handle</span> *<span class="title">fwnode</span>;</span></span><br><span class="line">	<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">property_entry</span> *<span class="title">properties</span>;</span></span><br><span class="line">	<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">resource</span> *<span class="title">resources</span>;</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">int</span>	num_resources;</span><br><span class="line">	<span class="type">int</span>		irq;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>type 和 addr 这两个成员变量是必须要设置的，一个是 I2C 设备的名字，一个是 I2C 设备的器件地址。我们看一下<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/arch/arm/mach-imx/mach-pcm037.c#L271">mach-pcm037.c</a>这个文件中，就有这种用法：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_board_info</span> <span class="title">pcm037_i2c_devices</span>[] =</span> &#123;</span><br><span class="line">	&#123;</span><br><span class="line">		I2C_BOARD_INFO(<span class="string">&quot;24c32&quot;</span>, <span class="number">0x52</span>), <span class="comment">/* E0=0, E1=1, E2=0 */</span></span><br><span class="line">		.properties = board_eeprom_properties,</span><br><span class="line">	&#125;, &#123;</span><br><span class="line">		I2C_BOARD_INFO(<span class="string">&quot;pcf8563&quot;</span>, <span class="number">0x51</span>),</span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>中使用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L429">I2C_BOARD_INFO</a> 来完成 pcm037_i2c_devices 的初始化工作， <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L429">I2C_BOARD_INFO</a> 是一个宏，定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> I2C_BOARD_INFO(dev_type, dev_addr) \</span></span><br><span class="line"><span class="meta">	.type = dev_type, .addr = (dev_addr)</span></span><br></pre></td></tr></table></figure>

<p>可以看出， I2C_BOARD_INFO 宏其实就是设置 i2c_board_info 的 type 和 addr 这两个成员变量，因此前面的主要工作就是设置 I2C 设备名字为 24c32， 24c32的器件地址为 0X52。可以在 Linux 源码里面全局搜索 i2c_board_info，会找到大量以 i2c_board_info 定义的I2C 设备信息，这些就是未使用设备树的时候 I2C 设备的描述方式，当采用了设备树以后就不会再使用 i2c_board_info 来描述 I2C 设备了。  </p>
<p>上面的变量只是定义一个全局变量，然后需要通过<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-boardinfo.c#L38">i2c_register_board_info()</a>函数创建一个i2c设备，我们来看一下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">i2c_register_board_info</span><span class="params">(<span class="type">int</span> busnum, <span class="keyword">struct</span> i2c_board_info <span class="type">const</span> *info, <span class="type">unsigned</span> len)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>示例如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">void</span> __init <span class="title function_">omap_h4_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	(...)</span><br><span class="line">	i2c_register_board_info(<span class="number">1</span>, h4_i2c_board_info,</span><br><span class="line">			ARRAY_SIZE(h4_i2c_board_info));</span><br><span class="line">	(...)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="2-2-方式二：显式地实例化设备"><a href="#2-2-方式二：显式地实例化设备" class="headerlink" title="2.2 方式二：显式地实例化设备"></a><font size=3>2.2 方式二：显式地实例化设备</font></h3><p>有时候无法知道该设备挂载哪个I2C bus下，无法知道它对应的I2C bus number。但是可以通过其他方法知道对应的i2c_adapter结构体。可以使用下面两个函数来创建i2c_client。</p>
<h4 id="2-2-1-i2c-new-device"><a href="#2-2-1-i2c-new-device" class="headerlink" title="2.2.1 i2c_new_device()"></a><font size=3>2.2.1 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L716">i2c_new_device()</a></font></h4><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L716">i2c_new_device()</a>函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> i2c_client *</span><br><span class="line"><span class="title function_">i2c_new_device</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adap, <span class="keyword">struct</span> i2c_board_info <span class="type">const</span> *info)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">&#125;</span><br><span class="line">EXPORT_SYMBOL_GPL(i2c_new_device);</span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L716">i2c_new_device()</a>函数会创建i2c_client，即使该设备并不存在。这里就不详细分析这个函数了，了解一下，示例如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_board_info</span> <span class="title">sfe4001_hwmon_info</span> =</span> &#123;</span><br><span class="line">	I2C_BOARD_INFO(<span class="string">&quot;max6647&quot;</span>, <span class="number">0x4e</span>),</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">sfe4001_init</span><span class="params">(<span class="keyword">struct</span> efx_nic *efx)</span></span><br><span class="line">&#123;</span><br><span class="line">	(...)</span><br><span class="line">	efx-&gt;board_info.hwmon_client =</span><br><span class="line">		i2c_new_device(&amp;efx-&gt;i2c_adap, &amp;sfe4001_hwmon_info);</span><br><span class="line"></span><br><span class="line">	(...)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="2-2-2-i2c-new-probed-device"><a href="#2-2-2-i2c-new-probed-device" class="headerlink" title="2.2.2 i2c_new_probed_device()"></a><font size=3>2.2.2 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L2199">i2c_new_probed_device()</a></font></h4><p>还可以使用<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L2199">i2c_new_probed_device()</a>函数，函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> i2c_client *</span><br><span class="line"><span class="title function_">i2c_new_probed_device</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adap,</span></span><br><span class="line"><span class="params">		      <span class="keyword">struct</span> i2c_board_info *info,</span></span><br><span class="line"><span class="params">		      <span class="type">unsigned</span> <span class="type">short</span> <span class="type">const</span> *addr_list,</span></span><br><span class="line"><span class="params">		      <span class="type">int</span> (*probe)(<span class="keyword">struct</span> i2c_adapter *, <span class="type">unsigned</span> <span class="type">short</span> addr))</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">&#125;</span><br><span class="line">EXPORT_SYMBOL_GPL(i2c_new_probed_device);</span><br></pre></td></tr></table></figure>

<p>这个函数成功的话，会创建i2c_client，并且表示这个设备肯定存在。I2C设备的地址可能发生变化，比如AT24C02的引脚A2A1A0电平不一样时，设备地址就不一样。它可以罗列出可能的地址，<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L2199">i2c_new_probed_device()</a>将会使用这些地址判断设备是否存在。这里也是简单了解下，示例如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">unsigned</span> <span class="type">short</span> normal_i2c[] = &#123; <span class="number">0x2c</span>, <span class="number">0x2d</span>, I2C_CLIENT_END &#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">usb_hcd_nxp_probe</span><span class="params">(<span class="keyword">struct</span> platform_device *pdev)</span></span><br><span class="line">&#123;</span><br><span class="line">	(...)</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">i2c_adapter</span> *<span class="title">i2c_adap</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">i2c_board_info</span> <span class="title">i2c_info</span>;</span></span><br><span class="line"></span><br><span class="line">	(...)</span><br><span class="line">	i2c_adap = i2c_get_adapter(<span class="number">2</span>);</span><br><span class="line">	<span class="built_in">memset</span>(&amp;i2c_info, <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="keyword">struct</span> i2c_board_info));</span><br><span class="line">	strlcpy(i2c_info.type, <span class="string">&quot;isp1301_nxp&quot;</span>, I2C_NAME_SIZE);</span><br><span class="line">	isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &amp;i2c_info,</span><br><span class="line">						   normal_i2c, <span class="literal">NULL</span>);</span><br><span class="line">	i2c_put_adapter(i2c_adap);</span><br><span class="line">	(...)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="2-3-方式三：探测特定设备的I2C总线"><a href="#2-3-方式三：探测特定设备的I2C总线" class="headerlink" title="2.3 方式三：探测特定设备的I2C总线"></a><font size=3>2.3 方式三：探测特定设备的I2C总线</font></h3><p>由i2c_driver.detect函数来判断是否有对应的I2C设备并生成i2c_client。好像不是很建议使用这种方式，这个详细的可以看<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/Documentation/i2c/instantiating-devices#L158">instantiating-devices - Method 3: Probe an I2C bus for certain devices</a>。</p>
<h3 id="2-4-方式四：从用户空间实例化"><a href="#2-4-方式四：从用户空间实例化" class="headerlink" title="2.4 方式四：从用户空间实例化"></a><font size=3>2.4 方式四：从用户空间实例化</font></h3><p>一般来说，内核应该知道连接了哪些I2C设备以及它们所在的地址。但是，在某些情况下，它不会这样做，因此添加了sysfs接口来让用户提供信息。该接口由2个属性文件组成，它们在每个I2C总线目录中创建：new_device和delete_device。这两个文件都只能写，我们必须向它们写入正确的参数，以便正确地实例化（或者删除）I2C设备。</p>
<ul>
<li>文件new_device接受2个参数：I2C设备的名称（一个字符串）和I2C设备的地址（一个数字，通常以0x开头的十六进制表示，但也可以用十进制表示）。我们创建一个i2c_client, .name &#x3D; “sdeveeprom”, .addr&#x3D;0x50, .adapter是i2c-1</li>
</ul>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">echo sdeveeprom 0x50 &gt; /sys/bus/i2c/devices/i2c-1/new_device</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330141349194.png" alt="image-20250330141349194"  />

<p>这个时候我们用i2cdetect工具看一下：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">i2cdetect -y -a 1</span><br></pre></td></tr></table></figure>

<p>会发现什么也没有，这个应该是因为我们实际并没有对应的i2c设备存在，这就会导致i2cdetect向这个地址发数据的时候，并不会收到回应，所以就探测不出来了</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330141544900.png" alt="image-20250330141544900" />

<ul>
<li>文件delete_device接受一个参数：I2C设备的地址。由于没有两个设备可以在给定的I2C段上使用相同的地址，因此地址足以唯一地标识要删除的设备。删除一个i2c_client</li>
</ul>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">echo 0x50 &gt; /sys/bus/i2c/devices/i2c-1/delete_device</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330141610365.png" alt="image-20250330141610365" />

<h3 id="2-5-i2c-client设备注册示例"><a href="#2-5-i2c-client设备注册示例" class="headerlink" title="2.5 i2c_client设备注册示例"></a><font size=3>2.5 i2c_client设备注册示例</font></h3><p>这里采用设备树的方式，以正点原子alpha开发板上的ap3216c为例，我们可以看一下原理图：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329162411698.png" alt="image-20250329162411698" style="zoom: 33%;" />

<p>可以看到，AP3216C芯片的SCL和SDA是接在I2C1上的。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&amp;i2c1 &#123;</span><br><span class="line">    clock-frequency = &lt;<span class="number">100000</span>&gt;;</span><br><span class="line">    pinctrl-names = <span class="string">&quot;default&quot;</span>;</span><br><span class="line">    pinctrl<span class="number">-0</span> = &lt;&amp;pinctrl_i2c1&gt;;</span><br><span class="line">    status = <span class="string">&quot;okay&quot;</span>;</span><br><span class="line"></span><br><span class="line">    ap3216c@<span class="number">1</span>e &#123;</span><br><span class="line">    	compatible = <span class="string">&quot;alpha,ap3216c&quot;</span>;</span><br><span class="line">    	reg = &lt;<span class="number">0x1e</span>&gt;;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>这两个引脚可以用作GPIO吗？我们可以看一下它们两个用的是什么引脚，看一下《 <a target="_blank" rel="noopener" href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh">i.MX 6ULL Applications Processor Reference Manual</a>》——Table 31-1. I2C External Signals  ：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329162949427.png" alt="image-20250329162949427"  />

<p>可以看到其实这两个引脚可以由GPIO1_IO02和GPIO1_03复用而来，从表上看，这两个gpio默认的功能就是I2C1的SCL和SDA。我们可以打开 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi#L328">imx6ul-14x14-evk.dtsi</a> 找一下pinctrl_i2c1：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">pinctrl_i2c1: i2c1grp &#123;</span><br><span class="line">		fsl,pins = &lt;</span><br><span class="line">			MX6UL_PAD_UART4_TX_DATA__I2C1_SCL <span class="number">0x4001b8b0</span></span><br><span class="line">			MX6UL_PAD_UART4_RX_DATA__I2C1_SDA <span class="number">0x4001b8b0</span></span><br><span class="line">		&gt;;</span><br><span class="line">	&#125;;</span><br></pre></td></tr></table></figure>

<p>可以发现，这里的I2C1用的是UART4的那两个引脚复用而来的。</p>
<h3 id="2-6-在sysfs中的体现"><a href="#2-6-在sysfs中的体现" class="headerlink" title="2.6 在sysfs中的体现"></a><font size=3>2.6 在sysfs中的体现</font></h3><p>当我们在设备树写好设备节点后，可以看一下这个 <code>/sys/bus/i2c/devices   </code>目录，这个目录下存放着所有的i2c设备。如果设备树修改正确的话，会在&#x2F;sys&#x2F;bus&#x2F;i2c&#x2F;devices 目录下看到一个名为“0-001e”的子目录 ：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330135916716.png" alt="image-20250330135916716" />

<p>“0-001e”就是 ap3216c 的设备目录，“1e”就是 ap3216c 器件地址。进入0-001e 目录，可以看到“name”文件， name 文件就保存着此设备名字，在这里就是“ap3216c” ：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330140012476.png" alt="image-20250330140012476" />

<p>我们还可以用i2cdetect工具探测一下：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">i2cdetect -y -a 0</span><br><span class="line">i2cdetect -y -a 1</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330140511185.png" alt="image-20250330140511185" />

<h2 id="3-I2C设备和驱动的匹配"><a href="#3-I2C设备和驱动的匹配" class="headerlink" title="3. I2C设备和驱动的匹配"></a><font size=3>3. I2C设备和驱动的匹配</font></h2><p>设备和驱动的匹配过程也是由 I2C 总线完成的， I2C 总线的数据结构为 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L493">i2c_bus_type</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">bus_type</span> <span class="title">i2c_bus_type</span> =</span> &#123;</span><br><span class="line">	.name		= <span class="string">&quot;i2c&quot;</span>,</span><br><span class="line">	.match		= i2c_device_match,</span><br><span class="line">	.probe		= i2c_device_probe,</span><br><span class="line">	.remove		= i2c_device_remove,</span><br><span class="line">	.shutdown	= i2c_device_shutdown,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>.match 就是 I2C 总线的设备和驱动匹配函数，在这里就是 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L101">i2c_device_match()</a> 这个函数 :</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">i2c_device_match</span><span class="params">(<span class="keyword">struct</span> device *dev, <span class="keyword">struct</span> device_driver *drv)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">i2c_client</span>	*<span class="title">client</span> =</span> i2c_verify_client(dev);</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">i2c_driver</span>	*<span class="title">driver</span>;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Attempt an OF style match */</span></span><br><span class="line">	<span class="keyword">if</span> (i2c_of_match_device(drv-&gt;of_match_table, client))</span><br><span class="line">		<span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Then ACPI style match */</span></span><br><span class="line">	<span class="keyword">if</span> (acpi_driver_match_device(dev, drv))</span><br><span class="line">		<span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">	driver = to_i2c_driver(drv);</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Finally an I2C match */</span></span><br><span class="line">	<span class="keyword">if</span> (i2c_match_id(driver-&gt;id_table, client))</span><br><span class="line">		<span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="3-1-设备树匹配：i2c-of-match-device"><a href="#3-1-设备树匹配：i2c-of-match-device" class="headerlink" title="3.1 设备树匹配：i2c_of_match_device()"></a><font size=3>3.1 设备树匹配：<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-of.c#L220">i2c_of_match_device()</a></font></h3><p>第一种方式是通过函数<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-of.c#L220">i2c_of_match_device()</a>完成设备树匹配：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="keyword">struct</span> of_device_id</span><br><span class="line">*<span class="title function_">i2c_of_match_device</span><span class="params">(<span class="type">const</span> <span class="keyword">struct</span> of_device_id *matches,</span></span><br><span class="line"><span class="params">		     <span class="keyword">struct</span> i2c_client *client)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">of_device_id</span> *<span class="title">match</span>;</span></span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (!(client &amp;&amp; matches))</span><br><span class="line">		<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">	match = of_match_device(matches, &amp;client-&gt;dev);</span><br><span class="line">	<span class="keyword">if</span> (match)</span><br><span class="line">		<span class="keyword">return</span> match;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> i2c_of_match_device_sysfs(matches, client);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在这个函数中，调用了两个函数，这两个函数都是使用<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L297">i2c_driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/device.h#L287">driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/mod_devicetable.h#L241">of_match_table</a>这个匹配表进行匹配。</p>
<ul>
<li>（1）<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/of/device.c#L18">of_match_device()</a></li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="keyword">struct</span> of_device_id *<span class="title function_">of_match_device</span><span class="params">(<span class="type">const</span> <span class="keyword">struct</span> of_device_id *matches,</span></span><br><span class="line"><span class="params">					   <span class="type">const</span> <span class="keyword">struct</span> device *dev)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">if</span> ((!matches) || (!dev-&gt;of_node))</span><br><span class="line">		<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">	<span class="keyword">return</span> of_match_node(matches, dev-&gt;of_node);</span><br><span class="line">&#125;</span><br><span class="line">EXPORT_SYMBOL(of_match_device);</span><br></pre></td></tr></table></figure>

<p>设备树中，某个I2C控制器节点下可以创建I2C设备的节点。如果I2C设备节点的compatible属性跟of_match_table的某项兼容，则匹配成功。</p>
<ul>
<li>（2）<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-of.c#L191">i2c_of_match_device_sysfs()</a></li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="keyword">struct</span> of_device_id*</span><br><span class="line"><span class="title function_">i2c_of_match_device_sysfs</span><span class="params">(<span class="type">const</span> <span class="keyword">struct</span> of_device_id *matches,</span></span><br><span class="line"><span class="params">				  <span class="keyword">struct</span> i2c_client *client)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">const</span> <span class="type">char</span> *name;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> (; matches-&gt;compatible[<span class="number">0</span>]; matches++) &#123;</span><br><span class="line">		<span class="comment">//......</span></span><br><span class="line">		<span class="keyword">if</span> (sysfs_streq(client-&gt;name, matches-&gt;compatible))</span><br><span class="line">			<span class="keyword">return</span> matches;</span><br><span class="line">		<span class="comment">//......</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个函数是比较<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L335">i2c_client</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L333">name</a>跟某个<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L297">i2c_driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/device.h#L287">driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/mod_devicetable.h#L241">of_match_table[i]</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/mod_devicetable.h#L244">compatible</a>值，若有相同的，则匹配成功。（这种方式在platform平台总线中好像没看到，但是i2c中是有这样的方式的）。<code>strchr()</code>函数是用于在字符串中查找第一次出现的指定字符，并返回一个指向该字符的指针。如果未找到字符，则返回<em>NULL</em>。</p>
<h3 id="3-2-id匹配：i2c-match-id"><a href="#3-2-id匹配：i2c-match-id" class="headerlink" title="3.2 id匹配：i2c_match_id()"></a><font size=3>3.2 id匹配：<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L86">i2c_match_id()</a></font></h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="keyword">struct</span> i2c_device_id *<span class="title function_">i2c_match_id</span><span class="params">(<span class="type">const</span> <span class="keyword">struct</span> i2c_device_id *id,</span></span><br><span class="line"><span class="params">						<span class="type">const</span> <span class="keyword">struct</span> i2c_client *client)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">if</span> (!(id &amp;&amp; client))</span><br><span class="line">		<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">while</span> (id-&gt;name[<span class="number">0</span>]) &#123;</span><br><span class="line">		<span class="keyword">if</span> (<span class="built_in">strcmp</span>(client-&gt;name, id-&gt;name) == <span class="number">0</span>)</span><br><span class="line">			<span class="keyword">return</span> id;</span><br><span class="line">		id++;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个函数用于传统的、无设备树的 I2C 设备和驱动匹配过程。  使用<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L298">i2c_driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/mod_devicetable.h#L436">id_table</a>来判断，当<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L335">i2c_client</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L333">name</a> 跟某个 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L298">i2c_driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/mod_devicetable.h#L436">id_table[i]</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/mod_devicetable.h#L437">name</a> 值相同，则匹配成功</p>
<p>i2c_driver跟i2c_client匹配成功后，就调用i2c_driver.probe函数。</p>
<h2 id="4-I2C设备收发数据"><a href="#4-I2C设备收发数据" class="headerlink" title="4. I2C设备收发数据"></a><font size=3>4. I2C设备收发数据</font></h2><h3 id="4-1-i2c-transfer"><a href="#4-1-i2c-transfer" class="headerlink" title="4.1 i2c_transfer()"></a><font size=3>4.1 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1913">i2c_transfer()</a></font></h3><p>I2C 设备驱动首先要做的就是初始化 i2c_driver 并向 Linux 内核注册。当设备和驱动匹配以后 i2c_driver 里面的 probe 函数就会执行， probe 函数里面所做的就是字符设备驱动那一套了。  </p>
<p>一般需要在 probe 函数里面初始化 I2C 设备，初始化完肯定就是对I2C设备进行操作了，在linux中为我们提供了 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1913">i2c_transfer()</a> 函数。 i2c_transfer 函数最终会调用 I2C 适配器中 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L497">i2c_algorithm</a> 里面的 <strong><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L526">master_xfer()</a></strong> 函数，对于imx6ull来说就是<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1042">i2c_imx_algo</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L883">i2c_imx_xfer()</a>，这个函数后面会进行分析，在里面就是完成了起始信号、结束信号、应答与非应答，读写等相关操作。  </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">i2c_transfer</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adap, <span class="keyword">struct</span> i2c_msg *msgs, <span class="type">int</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> ret;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">		ret = __i2c_transfer(adap, msgs, num);</span><br><span class="line">		i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">&#125;</span><br><span class="line">EXPORT_SYMBOL(i2c_transfer);</span><br></pre></td></tr></table></figure>

<p><strong>函数参数</strong></p>
<ul>
<li>adap： 所使用的 I2C 适配器， i2c_client 会保存其对应的 i2c_adapter。</li>
<li>msgs： <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/uapi/linux/i2c.h#L33">struct i2c_msg</a>类型指针，表示I2C 要发送的一个或多个消息。</li>
<li>num： 消息数量，也就是 msgs 的数量。</li>
</ul>
<p><strong>返回值：</strong> 负值，失败，其他非负值，发送的 msgs 数量。  </p>
<p>另外还有两个API函数<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L108">i2c_master_send()</a>和<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L78">i2c_master_recv()</a>，分别用于I2C数据的收发操作，这两个函数最终都会调用i2c_transfer。就相当于替我们封装了一下，这样就可以不用自己去定义i2c_msg了。</p>
<h3 id="4-2-收发示例"><a href="#4-2-收发示例" class="headerlink" title="4.2 收发示例"></a><font size=3>4.2 收发示例</font></h3><h4 id="4-2-1-设备结构体"><a href="#4-2-1-设备结构体" class="headerlink" title="4.2.1 设备结构体"></a><font size=3>4.2.1 设备结构体</font></h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 设备结构体 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">xxx_dev</span> &#123;</span></span><br><span class="line">    ......</span><br><span class="line">    <span class="type">void</span> *private_data; <span class="comment">/* 私有数据，一般会设置为 i2c_client */</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>设备结构体，在设备结构体里面添加一个执行void的指针成员变量private_data，此成员变量用于保存设备的私有数据。在 I2C 设备驱动中我们一般将其指向 I2C 设备对应的i2c_client。  </p>
<h4 id="4-2-2-xxx-read-regs"><a href="#4-2-2-xxx-read-regs" class="headerlink" title="4.2.2 xxx_read_regs()"></a><font size=3>4.2.2 xxx_read_regs()</font></h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @description : 读取 I2C 设备多个寄存器数据</span></span><br><span class="line"><span class="comment"> * @param – dev : I2C 设备</span></span><br><span class="line"><span class="comment"> * @param – reg : 要读取的寄存器首地址</span></span><br><span class="line"><span class="comment"> * @param – val : 读取到的数据</span></span><br><span class="line"><span class="comment"> * @param – len : 要读取的数据长度</span></span><br><span class="line"><span class="comment"> * @return : 操作结果</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">xxx_read_regs</span><span class="params">(<span class="keyword">struct</span> xxx_dev *dev, u8 reg, <span class="type">void</span> *val, <span class="type">int</span> len)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> ret;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">i2c_msg</span> <span class="title">msg</span>[2];</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">i2c_client</span> *<span class="title">client</span> =</span> (<span class="keyword">struct</span> i2c_client *)dev-&gt;private_data;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* msg[0]，第一条写消息，发送要读取的寄存器首地址 */</span></span><br><span class="line">    msg[<span class="number">0</span>].addr = client-&gt;addr; <span class="comment">/* I2C 器件地址 */</span></span><br><span class="line">    msg[<span class="number">0</span>].flags = <span class="number">0</span>;           <span class="comment">/* 标记为发送数据 */</span></span><br><span class="line">    msg[<span class="number">0</span>].buf = &amp;reg;          <span class="comment">/* 读取的首地址 */</span></span><br><span class="line">    msg[<span class="number">0</span>].len = <span class="number">1</span>;             <span class="comment">/* reg 长度 */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* msg[1]，第二条读消息，读取寄存器数据 */</span></span><br><span class="line">    msg[<span class="number">1</span>].addr = client-&gt;addr; <span class="comment">/* I2C 器件地址 */</span></span><br><span class="line">    msg[<span class="number">1</span>].flags = I2C_M_RD;    <span class="comment">/* 标记为读取数据 */</span></span><br><span class="line">    msg[<span class="number">1</span>].buf = val;           <span class="comment">/* 读取数据缓冲区 */</span></span><br><span class="line">    msg[<span class="number">1</span>].len = len;           <span class="comment">/* 要读取的数据长度 */</span></span><br><span class="line">    ret = i2c_transfer(client-&gt;adapter, msg, <span class="number">2</span>);</span><br><span class="line">    <span class="keyword">if</span> (ret == <span class="number">2</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        ret = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        ret = -EREMOTEIO;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>该函数用于读取 I2C 设备多个寄存器数据。</p>
<p>第 12 行：定义了一个i2c_msg 数组， 2 个数组元素，因为 I2C 读取数据的时候要先发送要读取的寄存器地址，然后再读取数据，所以需要准备两个 i2c_msg。一个用于发送寄存器地址，一个用于读取寄存器值。</p>
<p>第15 - 19 行：对于 msg[0]，将 flags 设置为 0，表示写数据。 msg[0]的 addr 是 I2C 设备的器件地址， msg[0]的 buf成员变量就是要读取的寄存器地址。    </p>
<p>第 21 - 25 行：对于 msg[1]，将 flags 设置为 I2C_M_RD，表示读取数据。msg[1]的 buf 成员变量用于保存读取到的数据， len 成员变量就是要读取的数据长度。</p>
<p>第26 - 34 行：调用i2c_transfer 函数完成 I2C 数据读操作。  </p>
<h4 id="4-2-3-xxx-write-regs"><a href="#4-2-3-xxx-write-regs" class="headerlink" title="4.2.3 xxx_write_regs()"></a><font size=3>4.2.3 xxx_write_regs()</font></h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @description : 向 I2C 设备多个寄存器写入数据</span></span><br><span class="line"><span class="comment"> * @param – dev : 要写入的设备结构体</span></span><br><span class="line"><span class="comment"> * @param – reg : 要写入的寄存器首地址</span></span><br><span class="line"><span class="comment"> * @param – buf : 要写入的数据缓冲区</span></span><br><span class="line"><span class="comment"> * @param – len : 要写入的数据长度</span></span><br><span class="line"><span class="comment"> * @return : 操作结果</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> s32 <span class="title function_">xxx_write_regs</span><span class="params">(<span class="keyword">struct</span> xxx_dev *dev, u8 reg, u8 *buf, u8 len)</span></span><br><span class="line">&#123;</span><br><span class="line">    u8 b[<span class="number">256</span>];</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">i2c_msg</span> <span class="title">msg</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">i2c_client</span> *<span class="title">client</span> =</span> (<span class="keyword">struct</span> i2c_client *)dev-&gt;private_data;</span><br><span class="line"></span><br><span class="line">    b[<span class="number">0</span>] = reg;              <span class="comment">/* 寄存器首地址 */</span></span><br><span class="line">    <span class="built_in">memcpy</span>(&amp;b[<span class="number">1</span>], buf, len); <span class="comment">/* 将要发送的数据拷贝到数组 b 里面 */</span></span><br><span class="line"></span><br><span class="line">    msg.addr = client-&gt;addr; <span class="comment">/* I2C 器件地址 */</span></span><br><span class="line">    msg.flags = <span class="number">0</span>;           <span class="comment">/* 标记为写数据 */</span></span><br><span class="line"></span><br><span class="line">    msg.buf = b;       <span class="comment">/* 要发送的数据缓冲区 */</span></span><br><span class="line">    msg.len = len + <span class="number">1</span>; <span class="comment">/* 要发送的数据长度 */</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> i2c_transfer(client-&gt;adapter, &amp;msg, <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>该函数用于向 I2C 设备多个寄存器写数据， I2C 写操作要比读操作简单一点，因此一个 i2c_msg 即可。</p>
<p>第 11 行：数组 b 用于存放寄存器首地址和要发送的数据。</p>
<p>第 18 行：设置 msg 的 addr 为 I2C 器件地址。</p>
<p>第 19 行：设置 msg 的 flags 为 0，也就是写数据。</p>
<p>第 21 行：设置要发送的数据，也就是数组 b。</p>
<p>第 22 行：设置 msg 的 len 为 len+1，因为要加上一个字节的寄存器地址。</p>
<p>第 24 行：最后通过 i2c_transfer 函数完成向 I2C 设备的写操作。  </p>
<h2 id="5-ap3216c实例"><a href="#5-ap3216c实例" class="headerlink" title="5. ap3216c实例"></a><font size=3>5. ap3216c实例</font></h2><h3 id="5-1-demo源码"><a href="#5-1-demo源码" class="headerlink" title="5.1 demo源码"></a><font size=3>5.1 demo源码</font></h3><p>demo源码可以看这里：<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/tree/master/30_i2c_subsystem/01_ap3216c">30_i2c_subsystem&#x2F;01_ap3216c · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a></p>
<h3 id="5-2-开发板测试"><a href="#5-2-开发板测试" class="headerlink" title="5.2 开发板测试"></a><font size=3>5.2 开发板测试</font></h3><p>我们更新对应的设备树，然后重启，就会发现在&#x2F;sys&#x2F;bus&#x2F;i2c&#x2F;devices&#x2F;下存在我们创建的ap3216c设备：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330173825291.png" alt="image-20250330173825291" />

<p>由于这设备室真实存在的，我们使用i2cdetect也会得到正确的回应：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">i2cdetect -y -a 0</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330173931777.png" alt="image-20250330173931777" />

<p>然后加载驱动：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">insmod sdriver_ap3216c.ko</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330174321446.png" alt="image-20250330174321446" />

<p>然后我们执行测试程序：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330174422138.png" alt="image-20250330174422138" />

<h1 id="二、I2C总线驱动分析"><a href="#二、I2C总线驱动分析" class="headerlink" title="二、I2C总线驱动分析"></a><font size=3>二、I2C总线驱动分析</font></h1><p>I2C总线驱动其实就是I2C 适配器驱动，也就是 SOC 的 I2C 控制器驱动。就相当于之前在裸机上控制I.MX6U的物理I2C外设。</p>
<p>I2C 设备驱动是需要用户根据不同的 I2C 设备去编写，而 I2C 适配器驱动一般都是 SOC 厂商去编写的，比如 NXP 就编写好了 I.MX6U 的I2C 适配器驱动。   </p>
<h2 id="1-I-MX6U-I2C-简介"><a href="#1-I-MX6U-I2C-简介" class="headerlink" title="1. I.MX6U I2C 简介  "></a><font size=3>1. I.MX6U I2C 简介  </font></h2><blockquote>
<p>可以参考《<a target="_blank" rel="noopener" href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh">i.MX 6ULL Applications Processor Reference Manual</a>》——Chapter 31 I2C Controller (I2C)</p>
</blockquote>
<p>I.MX6U 提供了 4 个 I2C 外设，通过这四个 I2C 外设即可完成与 I2C 从器件进行通信。I.MX6U 的 I2C 支持两种模式：标准模式和快速模式，标准模式下 I2C 数据传输速率最高是 100Kbits&#x2F;s，在快速模式下数据传输速率最高为 400Kbits&#x2F;s。它的内部框图如下：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250328113706039.png" alt="image-20250328113706039" style="zoom:50%;" />

<h3 id="1-1-I2Cx-IADR-x-1-4"><a href="#1-1-I2Cx-IADR-x-1-4" class="headerlink" title="1.1 I2Cx_IADR(x&#x3D;1~4)  "></a><font size=3>1.1 I2Cx_IADR(x&#x3D;1~4)  </font></h3><p>这个可以看《 <a target="_blank" rel="noopener" href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh">i.MX 6ULL Applications Processor Reference Manual</a>》—— 31.7.1 I2C Address Register (I2Cx_IADR)  ：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329105907331.png" alt="image-20250329105907331"  />

<p>寄存器 I2Cx_IADR 只有 ADR(bit7:1)位有效，用来保存 I2C 从设备地址数据。当我们要访问某个 I2C 从设备的时候就需要将其设备地址写入到 ADR 里面。  </p>
<h3 id="1-2-I2Cx-IFDR-x-1-4"><a href="#1-2-I2Cx-IFDR-x-1-4" class="headerlink" title="1.2 I2Cx_IFDR  (x&#x3D;1~4)  "></a><font size=3>1.2 I2Cx_IFDR  (x&#x3D;1~4)  </font></h3><p>这个可以看《 <a target="_blank" rel="noopener" href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh">i.MX 6ULL Applications Processor Reference Manual</a>》—— 31.7.2 I2C Frequency Divider Register (I2Cx_IFDR)  ：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329110046602.png" alt="image-20250329110046602"  />

<p>这个是 I2C 的分频寄存器，寄存器 I2Cx_IFDR 也只有 IC(bit5:0)这个位，用来设置 I2C 的波特率， I2C 的时钟源可以选择 IPG_CLK_ROOT&#x3D;66MHz，通过设置 IC 位既可以得到想要的 I2C 波特率。 IC 位可选的设置可以看《 <a target="_blank" rel="noopener" href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh">i.MX 6ULL Applications Processor Reference Manual</a>》—— Table 31-3. I2C_IFDR Register Field Values 。</p>
<p>不像其他外设的分频设置一样可以随意设置， Table 31-3. I2C_IFDR Register Field Values  中列出了 IC 的所有可选值。比如现在I2C的时钟源为66MHz，我们要设置I2C的波特率为100KHz，那么IC就可以设置为0X15，也就是 640 分频。 66000000&#x2F;640&#x3D;103.125KHz ≈ 100KHz。  </p>
<h3 id="1-3-I2Cx-I2CR-x-1-4"><a href="#1-3-I2Cx-I2CR-x-1-4" class="headerlink" title="1.3 I2Cx_I2CR    (x&#x3D;1~4)  "></a><font size=3>1.3 I2Cx_I2CR    (x&#x3D;1~4)  </font></h3><p>这个可以看《 <a target="_blank" rel="noopener" href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh">i.MX 6ULL Applications Processor Reference Manual</a>》—— 31.7.3 I2C Control Register (I2Cx_I2CR)，这个是 I2C 控制寄存器：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329110358912.png" alt="image-20250329110358912"  />

<p>存器 I2Cx_I2CR 的各位含义如下：</p>
<ul>
<li><p><strong>IEN(bit7)：</strong> I2C 使能位，为 1 的时候使能 I2C，为 0 的时候关闭 I2C。 </p>
</li>
<li><p><strong>IIEN(bit6)：</strong> I2C 中断使能位，为 1 的时候使能 I2C 中断，为 0 的时候关闭 I2C 中断。  </p>
</li>
<li><p><strong>MSTA(bit5)：</strong>主从模式选择位，设置 IIC 工作在主模式还是从模式，为 1 的时候工作在主模式，为 0 的时候工作在从模式。</p>
</li>
<li><p><strong>MTX(bit4)：</strong>传输方向选择位，用来设置是进行发送还是接收，为 0 的时候是接收，为 1 的时候是发送。</p>
</li>
<li><p><strong>TXAK(bit3)：</strong>传输应答位使能，为 0 的话发送 ACK 信号，为 1 的话发送 NO ACK 信号。</p>
</li>
<li><p><strong>RSTA(bit2)：</strong>重复开始信号，为 1 的话产生一个重新开始信号。</p>
</li>
</ul>
<h3 id="1-4-I2Cx-I2SR-x-1-4"><a href="#1-4-I2Cx-I2SR-x-1-4" class="headerlink" title="1.4 I2Cx_I2SR   (x&#x3D;1~4)  "></a><font size=3>1.4 I2Cx_I2SR   (x&#x3D;1~4)  </font></h3><p>这个可以看《 <a target="_blank" rel="noopener" href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh">i.MX 6ULL Applications Processor Reference Manual</a>》—— 31.7.4 I2C Status Register (I2Cx_I2SR)  ，这个是 I2C 的状态寄存器 ：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329110637057.png" alt="image-20250329110637057"  />

<p>寄存器 I2Cx_I2SR 的各位含义如下：  </p>
<ul>
<li><strong>ICF(bit7)：</strong>数据传输状态位，为 0 的时候表示数据正在传输，为 1 的时候表示数据传输完成。</li>
<li><strong>IAAS(bit6)：</strong>当为 1 的时候表示 I2C 地址，也就是 I2Cx_IADR 寄存器中的地址是从设备地址。</li>
<li><strong>IBB(bit5)：</strong> I2C 总线忙标志位，当为 0 的时候表示 I2C 总线空闲，为 1 的时候表示 I2C 总线忙。</li>
<li><strong>IAL(bit4)：</strong>仲裁丢失位，为 1 的时候表示发生仲裁丢失。</li>
<li><strong>SRW(bit2)：</strong>从机读写状态位，当 I2C 作为从机的时候使用，此位用来表明主机发送给从机的是读还是写命令。为 0 的时候表示主机要向从机写数据，为 1 的时候表示主机要从从机读取数据。</li>
<li><strong>IIF(bit1)：</strong> I2C 中断挂起标志位，当为 1 的时候表示有中断挂起，此位需要软件清零。</li>
<li><strong>RXAK(bit0)：</strong> 应答信号标志位，为 0 的时候表示接收到 ACK 应答信号，为 1 的话表示检测到 NO ACK 信号。</li>
</ul>
<h3 id="1-5-I2Cx-I2DR-x-1-4"><a href="#1-5-I2Cx-I2DR-x-1-4" class="headerlink" title="1.5 I2Cx_I2DR     (x&#x3D;1~4)  "></a><font size=3>1.5 I2Cx_I2DR     (x&#x3D;1~4)  </font></h3><p>这个可以看《 <a target="_blank" rel="noopener" href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh">i.MX 6ULL Applications Processor Reference Manual</a>》—— 31.7.5 I2C Data I&#x2F;O Register (I2Cx_I2DR)   ，这是 I2C 的数据寄存器，此寄存器只有低 8 位有效，当要发送数据的时候将要发送的数据写入到此寄存器，如果要接收数据的话直接读取此寄存器即可得到接收到的数据。  </p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329110910312.png" alt="image-20250329110910312"  />

<p>①、与标准 I2C 总线兼容。</p>
<p>②、多主机运行</p>
<p>③、软件可编程的 64 中不同的串行时钟序列。</p>
<p>④、软件可选择的应答位。⑤、开始&#x2F;结束信号生成和检测。⑥、重复开始信号生成。⑦、确认位生成。⑧、总线忙检测</p>
<h2 id="2-I2C1-控制器节点"><a href="#2-I2C1-控制器节点" class="headerlink" title="2.  I2C1 控制器节点"></a><font size=3>2.  I2C1 控制器节点</font></h2><p>在 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/arch/arm/boot/dts/imx6ul.dtsi#L886">imx6ul.dtsi</a> 文件中找到 I.MX6U 的 I2C1 控制器节点，节点内容如下所示：  </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">i2c1: i2c@<span class="number">21</span>a0000 &#123;</span><br><span class="line">	<span class="meta">#address-cells = <span class="string">&lt;1&gt;</span>;</span></span><br><span class="line">	<span class="meta">#size-cells = <span class="string">&lt;0&gt;</span>;</span></span><br><span class="line">	compatible = <span class="string">&quot;fsl,imx6ul-i2c&quot;</span>, <span class="string">&quot;fsl,imx21-i2c&quot;</span>;</span><br><span class="line">	reg = &lt;<span class="number">0x021a0000</span> <span class="number">0x4000</span>&gt;;</span><br><span class="line">	interrupts = &lt;GIC_SPI <span class="number">36</span> IRQ_TYPE_LEVEL_HIGH&gt;;</span><br><span class="line">	clocks = &lt;&amp;clks IMX6UL_CLK_I2C1&gt;;</span><br><span class="line">	status = <span class="string">&quot;disabled&quot;</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250328113804113.png" alt="image-20250328113804113" style="zoom:80%;" />

<p>可以看到，I2C1的起始地址就是0x021A_0000。不过这里重点关注 i2c1 节点的 compatible 属性值，因为通过 compatible 属性值可以在 Linux 源码里面找到对应的驱动文件。</p>
<p>这里i2c1节点的compatible属性值有两个：“fsl,imx6ul-i2c”和“fsl,imx21- i2c”，在 Linux 源码中搜索这两个字符串即可找到对应的驱动文件。    </p>
<h2 id="3-I2C适配器驱动"><a href="#3-I2C适配器驱动" class="headerlink" title="3. I2C适配器驱动"></a><font size=3>3. I2C适配器驱动</font></h2><p>我们打开 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1244">i2c-imx.c - drivers&#x2F;i2c&#x2F;busses&#x2F;i2c-imx.c</a>文件，找到这些内容：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">platform_driver</span> <span class="title">i2c_imx_driver</span> =</span> &#123;</span><br><span class="line">	.probe = i2c_imx_probe,</span><br><span class="line">	.remove = i2c_imx_remove,</span><br><span class="line">	.driver = &#123;</span><br><span class="line">		.name = DRIVER_NAME,</span><br><span class="line">		.pm = I2C_IMX_PM_OPS,</span><br><span class="line">		.of_match_table = i2c_imx_dt_ids,</span><br><span class="line">	&#125;,</span><br><span class="line">	.id_table = imx_i2c_devtype,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> __init <span class="title function_">i2c_adap_imx_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">return</span> platform_driver_register(&amp;i2c_imx_driver);</span><br><span class="line">&#125;</span><br><span class="line">subsys_initcall(i2c_adap_imx_init);</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> __exit <span class="title function_">i2c_adap_imx_exit</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	platform_driver_unregister(&amp;i2c_imx_driver);</span><br><span class="line">&#125;</span><br><span class="line">module_exit(i2c_adap_imx_exit);</span><br></pre></td></tr></table></figure>

<p>这部分是驱动的入口和退出函数，可以看到I.MX6U 的 I2C 适配器驱动是个标准的 platform 驱动，由此可以看出，虽然 I2C 总线为别的设备提供了一种总线驱动框架，但是 I2C 适配器却是 platform驱动。  </p>
<h3 id="3-1-i2c-imx-driver-of-match-table"><a href="#3-1-i2c-imx-driver-of-match-table" class="headerlink" title="3.1 i2c_imx_driver.of_match_table"></a><font size=3>3.1 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1250">i2c_imx_driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/device.h#L287">of_match_table</a></font></h3><p>既然是platform平台总线驱动，那么就和前面肯定是一样的，我们先看一下 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1250">i2c_imx_driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/device.h#L287">of_match_table</a>，可以看到它的值是 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L250">i2c_imx_dt_ids</a> ：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">of_device_id</span> <span class="title">i2c_imx_dt_ids</span>[] =</span> &#123;</span><br><span class="line">	&#123; .compatible = <span class="string">&quot;fsl,imx1-i2c&quot;</span>, .data = &amp;imx1_i2c_hwdata, &#125;,</span><br><span class="line">	&#123; .compatible = <span class="string">&quot;fsl,imx21-i2c&quot;</span>, .data = &amp;imx21_i2c_hwdata, &#125;,</span><br><span class="line">	&#123; .compatible = <span class="string">&quot;fsl,vf610-i2c&quot;</span>, .data = &amp;vf610_i2c_hwdata, &#125;,</span><br><span class="line">	&#123; <span class="comment">/* sentinel */</span> &#125;</span><br><span class="line">&#125;;</span><br><span class="line">MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);</span><br></pre></td></tr></table></figure>

<p>这里就是和设备树进行匹配的匹配表啦，前面i2c1的属性 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/arch/arm/boot/dts/imx6ul.dtsi#L889">compatible</a> ：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">compatible = <span class="string">&quot;fsl,imx6ul-i2c&quot;</span>, <span class="string">&quot;fsl,imx21-i2c&quot;</span>;</span><br></pre></td></tr></table></figure>

<p>所以这里就可以匹配上了。</p>
<h3 id="3-2-i2c-imx-driver-i2c-imx-probe"><a href="#3-2-i2c-imx-driver-i2c-imx-probe" class="headerlink" title="3.2 i2c_imx_driver. i2c_imx_probe"></a><font size=3>3.2 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1245">i2c_imx_driver</a>. <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1046">i2c_imx_probe</a></font></h3><p>按照platform平台总线的逻辑，当i2c适配器和驱动匹配后就会调用  <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1245">i2c_imx_driver</a>. <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1046">i2c_imx_probe</a>函数</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">i2c_imx_probe</span><span class="params">(<span class="keyword">struct</span> platform_device *pdev)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">// ......</span></span><br><span class="line">	<span class="comment">// 调用 platform_get_irq 函数获取中断号。</span></span><br><span class="line">	irq = platform_get_irq(pdev, <span class="number">0</span>);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="comment">// 调用 platform_get_resource 函数从设备树中获取 I2C1 控制器寄存器物理基地址，也就是 0X021A0000。</span></span><br><span class="line">	res = platform_get_resource(pdev, IORESOURCE_MEM, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">// 获取到寄存器基地址以后使用 devm_ioremap_resource 函数对其进行内存映射，得到可以在 Linux 内核中使用的虚拟地址。</span></span><br><span class="line">	base = devm_ioremap_resource(&amp;pdev-&gt;dev, res);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="comment">// NXP 使用 imx_i2c_struct 结构体来表示 I.MX 系列 SOC 的 I2C 控制器，这里使用 devm_kzalloc 函数来申请内存。</span></span><br><span class="line">	phy_addr = (<span class="type">dma_addr_t</span>)res-&gt;start;</span><br><span class="line">	i2c_imx = devm_kzalloc(&amp;pdev-&gt;dev, <span class="keyword">sizeof</span>(*i2c_imx), GFP_KERNEL);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="comment">/* Setup i2c_imx driver structure */</span></span><br><span class="line">    <span class="comment">// mx_i2c_struct 结构体要有个叫做 adapter 的成员变量， adapter 就是i2c_adapter，这里初始化i2c_adapter。</span></span><br><span class="line">	strlcpy(i2c_imx-&gt;adapter.name, pdev-&gt;name, <span class="keyword">sizeof</span>(i2c_imx-&gt;adapter.name));</span><br><span class="line">	i2c_imx-&gt;adapter.owner		= THIS_MODULE;</span><br><span class="line">	i2c_imx-&gt;adapter.algo		= &amp;i2c_imx_algo; <span class="comment">// 设置i2c_adapter的algo成员变量为i2c_imx_algo，也就是设置 i2c_algorithm。</span></span><br><span class="line">	i2c_imx-&gt;adapter.dev.parent	= &amp;pdev-&gt;dev;</span><br><span class="line">	i2c_imx-&gt;adapter.nr		= pdev-&gt;id;</span><br><span class="line">	i2c_imx-&gt;adapter.dev.of_node	= pdev-&gt;dev.of_node;</span><br><span class="line">	i2c_imx-&gt;base			= base;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Get I2C clock */</span></span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Request IRQ */</span></span><br><span class="line">    <span class="comment">//注册 I2C 控制器中断，中断服务函数为 i2c_imx_isr。</span></span><br><span class="line">	ret = devm_request_irq(&amp;pdev-&gt;dev, irq, i2c_imx_isr, IRQF_SHARED,</span><br><span class="line">				pdev-&gt;name, i2c_imx);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="comment">/* Set up clock divider */</span></span><br><span class="line">    <span class="comment">//设置 I2C 频率默认为 IMX_I2C_BIT_RATE=100KHz，如果设备树节点设置了“clock-frequency”属性的话 I2C 频率就使用 clock-frequency 属性值。</span></span><br><span class="line">	i2c_imx-&gt;bitrate = IMX_I2C_BIT_RATE;</span><br><span class="line">	ret = of_property_read_u32(pdev-&gt;dev.of_node,</span><br><span class="line">				   <span class="string">&quot;clock-frequency&quot;</span>, &amp;i2c_imx-&gt;bitrate);</span><br><span class="line">	<span class="keyword">if</span> (ret &lt; <span class="number">0</span> &amp;&amp; pdata &amp;&amp; pdata-&gt;bitrate)</span><br><span class="line">		i2c_imx-&gt;bitrate = pdata-&gt;bitrate;</span><br><span class="line">	i2c_imx-&gt;clk_change_nb.notifier_call = i2c_imx_clk_notifier_call;</span><br><span class="line">	clk_notifier_register(i2c_imx-&gt;clk, &amp;i2c_imx-&gt;clk_change_nb);</span><br><span class="line">	i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx-&gt;clk));</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Set up chip registers to defaults */</span></span><br><span class="line">    <span class="comment">// 设置 I2C1 控制的 I2CR 和 I2SR 寄存器。</span></span><br><span class="line">	imx_i2c_write_reg(i2c_imx-&gt;hwdata-&gt;i2cr_ien_opcode ^ I2CR_IEN,</span><br><span class="line">			i2c_imx, IMX_I2C_I2CR);</span><br><span class="line">	imx_i2c_write_reg(i2c_imx-&gt;hwdata-&gt;i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);</span><br><span class="line"></span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="comment">/* Add I2C adapter */</span></span><br><span class="line">    <span class="comment">//调用 i2c_add_numbered_adapter 函数向 Linux 内核注册 i2c_adapter。</span></span><br><span class="line">	ret = i2c_add_numbered_adapter(&amp;i2c_imx-&gt;adapter);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="comment">/* Init DMA config if supported */</span></span><br><span class="line">    <span class="comment">//申请 DMA， 看来 I.MX 的 I2C 适配器驱动采用了 DMA 方式。</span></span><br><span class="line">	i2c_imx_dma_request(i2c_imx, phy_addr);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;   <span class="comment">/* Return OK */</span></span><br><span class="line">	<span class="comment">//.......</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>i2c_imx_probe 函数主要的工作就是一下两点：①、初始化 i2c_adapter，设置 i2c_algorithm 为 i2c_imx_algo，最后向 Linux 内核注册i2c_adapter。②、初始化 I2C1 控制器的相关寄存器。</p>
<h3 id="3-3-i2c-imx-algo"><a href="#3-3-i2c-imx-algo" class="headerlink" title="3.3 i2c_imx_algo"></a><font size=3>3.3 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1041">i2c_imx_algo</a></font></h3><p>我们来看一下 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1046">i2c_imx_probe()</a>函数的<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1084">1084</a>行：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">i2c_imx-&gt;adapter.algo		= &amp;i2c_imx_algo;</span><br></pre></td></tr></table></figure>

<p>这里是在设置 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L670">i2c_adapter</a>的algo成员变量为 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1041">i2c_imx_algo</a> ，也就是设置 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L497">i2c_algorithm</a>。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_algorithm</span> <span class="title">i2c_imx_algo</span> =</span> &#123;</span><br><span class="line">	.master_xfer	= i2c_imx_xfer, <span class="comment">// 完成与 I2C 设备通信的</span></span><br><span class="line">	.functionality	= i2c_imx_func, <span class="comment">// functionality用于返回此I2C适配器支持什么样的通信协议</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>前面我们就知道<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L497">i2c_algorithm</a>中的关键函数**<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L497">i2c_algorithm</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L526">master_xfer()</a>**用于产生i2c访问周期需要的start stop ack信号，以i2c_msg（即i2c消息）为单位发送和接收通信数据。在这里就对应 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L1042">i2c_imx_algo</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L883">i2c_imx_xfer()</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">i2c_imx_xfer</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adapter,</span></span><br><span class="line"><span class="params">						<span class="keyword">struct</span> i2c_msg *msgs, <span class="type">int</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="comment">/* Start I2C transfer 开启 I2C 通信 */</span></span><br><span class="line">	result = i2c_imx_start(i2c_imx);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="comment">/* read/write data */</span></span><br><span class="line">	<span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; num; i++) &#123;</span><br><span class="line">		<span class="keyword">if</span> (i == num - <span class="number">1</span>)</span><br><span class="line">			is_lastmsg = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (i) &#123;</span><br><span class="line">			dev_dbg(&amp;i2c_imx-&gt;adapter.dev,</span><br><span class="line">				<span class="string">&quot;&lt;%s&gt; repeated start\n&quot;</span>, __func__);</span><br><span class="line">			temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);</span><br><span class="line">			temp |= I2CR_RSTA;</span><br><span class="line">			imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);</span><br><span class="line">			result = i2c_imx_bus_busy(i2c_imx, <span class="number">1</span>);</span><br><span class="line">			<span class="keyword">if</span> (result)</span><br><span class="line">				<span class="keyword">goto</span> fail0;</span><br><span class="line">		&#125;</span><br><span class="line">		dev_dbg(&amp;i2c_imx-&gt;adapter.dev,</span><br><span class="line">			<span class="string">&quot;&lt;%s&gt; transfer message: %d\n&quot;</span>, __func__, i);</span><br><span class="line">		<span class="comment">/* write/read data */</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_I2C_DEBUG_BUS</span></span><br><span class="line">		<span class="comment">//......</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">		<span class="keyword">if</span> (msgs[i].flags &amp; I2C_M_RD)</span><br><span class="line">			result = i2c_imx_read(i2c_imx, &amp;msgs[i], is_lastmsg);</span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			<span class="keyword">if</span> (i2c_imx-&gt;dma &amp;&amp; msgs[i].len &gt;= DMA_THRESHOLD)</span><br><span class="line">				result = i2c_imx_dma_write(i2c_imx, &amp;msgs[i]);</span><br><span class="line">			<span class="keyword">else</span></span><br><span class="line">				result = i2c_imx_write(i2c_imx, &amp;msgs[i]);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span> (result)</span><br><span class="line">			<span class="keyword">goto</span> fail0;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">fail0:</span><br><span class="line">	<span class="comment">/* Stop I2C transfer 停止 I2C 通信 */</span></span><br><span class="line">	i2c_imx_stop(i2c_imx);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="keyword">return</span> (result &lt; <span class="number">0</span>) ? result : num;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里涉及到这几个函数：<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L523">i2c_imx_start</a>、 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L778">i2c_imx_read</a>、 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L475">i2c_imx_write</a> 和 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-imx.c#L552">i2c_imx_stop()</a>，这里就是和裸机中i2c的驱动是一样的了，可以参考<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-bare-demo/blob/master/20_i2c/01_i2c_demo/bsp/i2c/bsp_i2c.c">20_i2c&#x2F;01_i2c_demo&#x2F;bsp&#x2F;i2c&#x2F;bsp_i2c.c · 苏木&#x2F;imx6ull-bare-demo - 码云 - 开源中国</a>这个demo中的i2c外设的操作进行理解。不过这里都是物理I2C的操作，并不是用GPIO模拟的，所以都是在操作I2C外设相关寄存器。<br><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/072_master_xfer_imx6ull.png" alt="image-20210315141442957"></p>
<h3 id="3-4-i2c-adapter注册-注销函数"><a href="#3-4-i2c-adapter注册-注销函数" class="headerlink" title="3.4 i2c_adapter注册&#x2F;注销函数"></a><font size=3>3.4 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L670">i2c_adapter</a>注册&#x2F;注销函数</font></h3><h4 id="3-4-1-i2c-add-adapter"><a href="#3-4-1-i2c-add-adapter" class="headerlink" title="3.4.1 i2c_add_adapter()"></a><font size=3>3.4.1 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1346">i2c_add_adapter()</a></font></h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">i2c_add_adapter</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adapter)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="3-4-2-i2c-add-numbered-adapter"><a href="#3-4-2-i2c-add-numbered-adapter" class="headerlink" title="3.4.2 i2c_add_numbered_adapter()"></a><font size=3>3.4.2 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1372">i2c_add_numbered_adapter()</a></font></h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">i2c_add_numbered_adapter</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adap)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="3-4-3-i2c-del-adapter"><a href="#3-4-3-i2c-del-adapter" class="headerlink" title="3.4.3 i2c_del_adapter()"></a><font size=3>3.4.3 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1450">i2c_del_adapter()</a></font></h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">i2c_del_adapter</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adap)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="4-编写一个虚拟的i2c-adapter"><a href="#4-编写一个虚拟的i2c-adapter" class="headerlink" title="4. 编写一个虚拟的i2c_adapter"></a><font size=3>4. 编写一个虚拟的<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L670">i2c_adapter</a></font></h2><h3 id="4-1-设备树"><a href="#4-1-设备树" class="headerlink" title="4.1 设备树"></a><font size=3>4.1 设备树</font></h3><p>在设备树根节点里构造I2C Bus节点：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">/ &#123;</span><br><span class="line">	i2c-bus-virtual &#123;</span><br><span class="line">		 compatible = <span class="string">&quot;alpha,i2c-bus-virtual&quot;</span>;</span><br><span class="line">	&#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>这个其实就相当于<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L670">i2c_adapter</a>的设备。</p>
<h3 id="4-2-适配器的驱动"><a href="#4-2-适配器的驱动" class="headerlink" title="4.2 适配器的驱动"></a><font size=3>4.2 适配器的驱动</font></h3><h4 id="4-2-1-要做哪些？"><a href="#4-2-1-要做哪些？" class="headerlink" title="4.2.1 要做哪些？"></a><font size=3>4.2.1 要做哪些？</font></h4><p>前面我们知道 I2C 适配器驱动是个标准的 platform 驱动，所以这里其实就按照 platform 驱动的编写流程，进行分配、设置、注册platform_driver结构体即可。核心是probe函数，它要做这几件事：</p>
<ul>
<li>根据设备树信息设置硬件(引脚、时钟等)</li>
<li>分配、设置、注册i2c_apdater，i2c_apdater的核心是master_xfer函数，它的实现取决于硬件，大概代码如下</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">xxx_master_xfer</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adapter,</span></span><br><span class="line"><span class="params">						<span class="keyword">struct</span> i2c_msg *msgs, <span class="type">int</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; num; i++) &#123;</span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">i2c_msg</span> *<span class="title">msg</span> =</span> msgs[i];</span><br><span class="line">        &#123;</span><br><span class="line">        	<span class="comment">// 1. 发出S信号: 设置寄存器发出S信号</span></span><br><span class="line">            CTLREG = S;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 2. 根据Flag发出设备地址和R/W位: 把这8位数据写入某个DATAREG即可发出信号</span></span><br><span class="line">            <span class="comment">//    判断是否有ACK</span></span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> (!ACK)</span><br><span class="line">                <span class="keyword">return</span> ERROR;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">	            <span class="comment">// 3. read / write</span></span><br><span class="line">	            <span class="keyword">if</span> (read) &#123;</span><br><span class="line">                    STATUS = XXX; <span class="comment">// 这决定读到一个数据后是否发出ACK给对方</span></span><br><span class="line">                    val = DATAREG; <span class="comment">// 这会发起I2C读操作</span></span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span>(write) &#123;</span><br><span class="line">                    DATAREG = val; <span class="comment">// 这会发起I2C写操作</span></span><br><span class="line">                    val = STATUS;  <span class="comment">// 判断是否收到ACK</span></span><br><span class="line">                    <span class="keyword">if</span> (!ACK)</span><br><span class="line">                        <span class="keyword">return</span> ERROR;</span><br><span class="line">                &#125;                </span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 4. 发出P信号</span></span><br><span class="line">            CTLREG = P;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> i;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="4-2-2-i2c-adapter框架"><a href="#4-2-2-i2c-adapter框架" class="headerlink" title="4.2.2 i2c_adapter框架"></a><font size=3>4.2.2 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L670">i2c_adapter</a>框架</font></h4><p>框架代码可以看这里：<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/blob/master/30_i2c_subsystem/02_i2c_adapter_virtual/drivers_demo/sdriver_demo.c">30_i2c_subsystem&#x2F;02_i2c_adapter_virtual&#x2F;drivers_demo&#x2F;sdriver_demo.c · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a>，更新设备树，然后加载驱动，我们用i2cdetect列出所有的i2c总线：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">i2cdetect -l</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330145705461.png" alt="image-20250330145705461" />

<p><strong>注意</strong>：不同的板子上，i2c-bus-virtual的总线号可能不一样，上问中总线号是4。</p>
<p>我们在这个总线上创建一个i2c设备：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">echo sdeveeprom 0x50 &gt; /sys/bus/i2c/devices/i2c-4/new_device</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330163641600.png" alt="image-20250330163641600" />

<p>发现也是可以的。</p>
<h4 id="4-2-3-模拟EEPROM"><a href="#4-2-3-模拟EEPROM" class="headerlink" title="4.2.3 模拟EEPROM"></a><font size=3>4.2.3 模拟EEPROM</font></h4><p>在前面完成的虚拟的I2C_Adapter驱动框架里，只要实现了其中的master_xfer函数，这个I2C Adapter就可以使用了。在master_xfer函数里，我们模拟一个EEPROM，思路如下：</p>
<ul>
<li>分配一个512字节的buffer，表示EEPROM</li>
<li>对于slave address为0x50的i2c_msg，解析并处理</li>
</ul>
<p>对于写：把i2c_msg的数据写入buffer。对于读：从buffer中把数据写入i2c_msg。</p>
<ul>
<li>对于slave address为其他值的i2c_msg，返回错误</li>
</ul>
<p>完整的demo看这里：<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/tree/master/30_i2c_subsystem/03_i2c_adapter_virtual_ok">30_i2c_subsystem&#x2F;03_i2c_adapter_virtual_ok · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a></p>
<h3 id="4-3-模拟EEPROM-demo"><a href="#4-3-模拟EEPROM-demo" class="headerlink" title="4.3 模拟EEPROM demo"></a><font size=3>4.3 模拟EEPROM demo</font></h3><h4 id="4-3-1-demo源码"><a href="#4-3-1-demo源码" class="headerlink" title="4.3.1 demo源码"></a><font size=3>4.3.1 demo源码</font></h4><p>demo源码看这里：<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/tree/master/30_i2c_subsystem/03_i2c_adapter_virtual_ok">30_i2c_subsystem&#x2F;03_i2c_adapter_virtual_ok · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a></p>
<h4 id="4-3-2-测试结果"><a href="#4-3-2-测试结果" class="headerlink" title="4.3.2 测试结果"></a><font size=3>4.3.2 测试结果</font></h4><p>更新开发板中的设备树，然后应该可以<code>i2c/devices/</code>在看到对应的i2c适配器：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330164220088.png" alt="image-20250330164220088" />

<p>然后我们使用i2c-tools进行测试：</p>
<ul>
<li>列出I2C总线</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">i2cdetect -l</span><br></pre></td></tr></table></figure>

<p>结果类似下列的信息：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330164312223.png" alt="image-20250330164312223" />

<p><strong>注意</strong>：不同的板子上，i2c-bus-virtual的总线号可能不一样，上问中总线号是4。</p>
<ul>
<li>检查虚拟总线下的I2C设备</li>
</ul>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">假设虚拟I2C BUS号为4</span></span><br><span class="line">i2cdetect -y -a 4</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330164613670.png" alt="image-20250330164613670" />

<p>这里似乎并不需要自己创建设备。</p>
<ul>
<li>读写模拟的EEPROM</li>
</ul>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">i2cset -f -y 4 0x50 0 0x55   # 假设虚拟I2C BUS号为4 往0地址写入0x55</span><br><span class="line">i2cget -f -y 4 0x50 0        # 读0地址</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330164711276.png" alt="image-20250330164711276" />

<h1 id="三、GPIO模拟I2C"><a href="#三、GPIO模拟I2C" class="headerlink" title="三、GPIO模拟I2C"></a><font size=3>三、GPIO模拟I2C</font></h1><h2 id="1-GPIO模拟I2C简介"><a href="#1-GPIO模拟I2C简介" class="headerlink" title="1. GPIO模拟I2C简介"></a><font size=3>1. GPIO模拟I2C简介</font></h2><p>在Linux项目中，如果出现硬件I2C不够用的情况，就可以使用模拟i2c来解决。其实I2C只是规定了数据传输的协议，并没有说一定要使用物理的I2C外设，我们知道2C协议信号如下：</p>
<p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/009_i2c_signal.png" alt="image-20210220151524099"></p>
<p>这里其实就是一根时钟线，一根数据线，这些我们完全可以用两个GPIO来进行模拟。之前学习单片机的时候其实一开始用的都是gpio模拟i2c：<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/stm32f103-prj/blob/master/STM32_HAL_Prj/Drivers/BSP/IIC/myiic.c">STM32_HAL_Prj&#x2F;Drivers&#x2F;BSP&#x2F;IIC&#x2F;myiic.c · 苏木&#x2F;STM32F103-Prj - 码云 - 开源中国</a></p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329131418887.png" alt="image-20250329131418887"  />

<p>使用GPIO模拟I2C的要点：</p>
<ul>
<li>引脚设为GPIO。</li>
<li>GPIO设为输出、开极&#x2F;开漏(open collector&#x2F;open drain)。</li>
<li>要有上拉电阻，为什么需要上拉电阻，在前面已经学习过了，主要就是可以方便的实现数据双向传输。以及实现多个设备对I2C总线的使用。</li>
</ul>
<h2 id="2-GPIO引脚定义"><a href="#2-GPIO引脚定义" class="headerlink" title="2. GPIO引脚定义"></a><font size=3>2. GPIO引脚定义</font></h2><p>既然是GPIO模拟I2C，那其实，只需要两个I2C就可以了，但是在不同版本的内核中，会支持一些新的写法。</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/Documentation/devicetree/bindings/i2c/i2c-gpio.txt">i2c-gpio.txt - Documentation&#x2F;devicetree&#x2F;bindings&#x2F;i2c&#x2F;i2c-gpio.txt - Linux source code v4.19.71</a></li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">Example nodes:</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;dt-bindings/gpio/gpio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line">i2c@<span class="number">0</span> &#123;</span><br><span class="line">	compatible = <span class="string">&quot;i2c-gpio&quot;</span>;</span><br><span class="line">	sda-gpios = &lt;&amp;pioA <span class="number">23</span> (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)&gt;;</span><br><span class="line">	scl-gpios = &lt;&amp;pioA <span class="number">24</span> (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)&gt;;</span><br><span class="line">	i2c-gpio,delay-us = &lt;<span class="number">2</span>&gt;;	<span class="comment">/* ~100 kHz */</span></span><br><span class="line">	<span class="meta">#address-cells = <span class="string">&lt;1&gt;</span>;</span></span><br><span class="line">	<span class="meta">#size-cells = <span class="string">&lt;0&gt;</span>;</span></span><br><span class="line"></span><br><span class="line">	rv3029c2@<span class="number">56</span> &#123;</span><br><span class="line">		compatible = <span class="string">&quot;rv3029c2&quot;</span>;</span><br><span class="line">		reg = &lt;<span class="number">0x56</span>&gt;;</span><br><span class="line">	&#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<ul>
<li><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.1.15/source/Documentation/devicetree/bindings/i2c/i2c-gpio.txt">i2c-gpio.txt - Documentation&#x2F;devicetree&#x2F;bindings&#x2F;i2c&#x2F;i2c-gpio.txt - Linux source code v4.1.15</a></li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">i2c@<span class="number">0</span> &#123;</span><br><span class="line">	compatible = <span class="string">&quot;i2c-gpio&quot;</span>;</span><br><span class="line">	gpios = &lt;&amp;pioA <span class="number">23</span> <span class="number">0</span> <span class="comment">/* sda */</span></span><br><span class="line">		 &amp;pioA <span class="number">24</span> <span class="number">0</span> <span class="comment">/* scl */</span></span><br><span class="line">		&gt;;</span><br><span class="line">	i2c-gpio,sda-open-drain;</span><br><span class="line">	i2c-gpio,scl-open-drain;</span><br><span class="line">	i2c-gpio,delay-us = &lt;<span class="number">2</span>&gt;;	<span class="comment">/* ~100 kHz */</span></span><br><span class="line">	<span class="meta">#address-cells = <span class="string">&lt;1&gt;</span>;</span></span><br><span class="line">	<span class="meta">#size-cells = <span class="string">&lt;0&gt;</span>;</span></span><br><span class="line"></span><br><span class="line">	rv3029c2@<span class="number">56</span> &#123;</span><br><span class="line">		compatible = <span class="string">&quot;rv3029c2&quot;</span>;</span><br><span class="line">		reg = &lt;<span class="number">0x56</span>&gt;;</span><br><span class="line">	&#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="3-i2c-gpio基本框架"><a href="#3-i2c-gpio基本框架" class="headerlink" title="3. i2c-gpio基本框架"></a><font size=3>3. i2c-gpio基本框架</font></h2><p>Lnux内核的<strong>i2c-gpio</strong>是使用GPIO模拟I2C协议的驱动，在内核中已经实现了，我们要做的只需要配置2个GPIO（SDA和SCL）即可。</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329140210360.png" alt="image-20250329140210360"  />

<ul>
<li><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c">i2c-gpio.c - drivers&#x2F;i2c&#x2F;busses&#x2F;i2c-gpio.c</a></li>
</ul>
<p>（1）解析设备树中的引脚配置信息</p>
<p>（2）提供GPIO SDA和SCL引脚配置接口。</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c">i2c-algo-bit.c - drivers&#x2F;i2c&#x2F;algos&#x2F;i2c-algo-bit.c</a></li>
</ul>
<p>（1）向I2C Core注册一个adapter</p>
<p>（2）提供I2C通信时的算法，然后通过<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c">i2c-gpio.c</a>提供GPIO配置接口来收发数据。</p>
<blockquote>
<p>注册成功后，<code>&quot;i2c-dev&quot;</code>驱动就会自动创建对应的<code>&quot;/dev/i2c-x&quot;</code>字符设备，然后我们就可以在应用层和驱动层操作该总线。</p>
</blockquote>
<h2 id="4-如何添加一个模拟I2C？"><a href="#4-如何添加一个模拟I2C？" class="headerlink" title="4. 如何添加一个模拟I2C？"></a><font size=3>4. 如何添加一个模拟I2C？</font></h2><h3 id="4-1-驱动相关文件放在那里？"><a href="#4-1-驱动相关文件放在那里？" class="headerlink" title="4.1 驱动相关文件放在那里？"></a><font size=3>4.1 驱动相关文件放在那里？</font></h3><p>GPIO模拟I2C协议的驱动位于 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses">busses - drivers&#x2F;i2c&#x2F;busses</a> 目录。驱动名称为“i2c-gpio”，驱动文件为<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c">i2c-gpio.c - drivers&#x2F;i2c&#x2F;busses&#x2F;i2c-gpio.c</a>，这个也是platform驱动：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329135722971.png" alt="image-20250329135722971"  />

<h3 id="4-2-使能内核的I2C-GPIO驱动"><a href="#4-2-使能内核的I2C-GPIO驱动" class="headerlink" title="4.2 使能内核的I2C GPIO驱动"></a><font size=3>4.2 使能内核的I2C GPIO驱动</font></h3><p>我们在内核源码目录下输入：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig</span><br><span class="line">make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig</span><br></pre></td></tr></table></figure>

<p>按以下路径找到对应的配置项：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Device Drivers-&gt;</span><br><span class="line">    I2C support  ---&gt;</span><br><span class="line">        I2C Hardware Bus support  ---&gt;</span><br><span class="line">            &lt;*&gt; GPIO-based bitbanging I2C</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250329140631954.png" alt="image-20250329140631954"  />

<p>确认配置后，<code>i2c-gpio</code>相关驱动就会被编译进内核。当然我们也可以编译成驱动模块，然后手动加载，不过还是直接编译到内核更方便一些。</p>
<h3 id="4-3-设备树配置"><a href="#4-3-设备树配置" class="headerlink" title="4.3 设备树配置"></a><font size=3>4.3 设备树配置</font></h3><h4 id="4-3-1-模拟I2C节点"><a href="#4-3-1-模拟I2C节点" class="headerlink" title="4.3.1 模拟I2C节点"></a><font size=3>4.3.1 模拟I2C节点</font></h4><p>i2c-gpio的<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L670">i2c_adapter</a>设备树节点这样写：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">i2c5:i2c5_gpio &#123;</span><br><span class="line">	<span class="meta">#address-cells = <span class="string">&lt;1&gt;</span>;</span></span><br><span class="line">	<span class="meta">#size-cells = <span class="string">&lt;0&gt;</span>;</span></span><br><span class="line">	compatible = <span class="string">&quot;i2c-gpio&quot;</span>;</span><br><span class="line">	gpios = &lt;&amp;gpio1 <span class="number">29</span> GPIO_ACTIVE_HIGH&gt;, 	<span class="comment">/* sda */</span></span><br><span class="line">			&lt;&amp;gpio1 <span class="number">28</span> GPIO_ACTIVE_HIGH&gt;; 	<span class="comment">/* scl */</span></span><br><span class="line">	i2c-gpio,delay-us = &lt;<span class="number">5</span>&gt;;		<span class="comment">/* ~100 kHz */</span></span><br><span class="line">	status = <span class="string">&quot;disabled&quot;</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>我们打开<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/arch/arm/boot/dts/imx6ul.dtsi#L886">imx6ul.dtsi</a>添加以上内容，需要注意的是添加需要模拟i2c的gpio的时候，一定是先放<strong>sda</strong>再放<strong>scl</strong>，因为它是在<code>i2c-gpio.c</code>里面定义好的，必须这么写才可以。</p>
<h4 id="4-3-2-修改aliases"><a href="#4-3-2-修改aliases" class="headerlink" title="4.3.2 修改aliases"></a><font size=3>4.3.2 修改aliases</font></h4><p>这里以 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/arch/arm/boot/dts/imx6ul.dtsi#L34">imx6ul.dtsi - arch&#x2F;arm&#x2F;boot&#x2F;dts&#x2F;imx6ul.dtsi</a> 为例，我们添加如下内容：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">/ &#123;</span><br><span class="line">	<span class="meta">#address-cells = <span class="string">&lt;1&gt;</span>;</span></span><br><span class="line">	<span class="meta">#size-cells = <span class="string">&lt;1&gt;</span>;</span></span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	aliases &#123;</span><br><span class="line">		ethernet0 = &amp;fec1;</span><br><span class="line">		ethernet1 = &amp;fec2;</span><br><span class="line">		gpio0 = &amp;gpio1;</span><br><span class="line">		gpio1 = &amp;gpio2;</span><br><span class="line">		gpio2 = &amp;gpio3;</span><br><span class="line">		gpio3 = &amp;gpio4;</span><br><span class="line">		gpio4 = &amp;gpio5;</span><br><span class="line">		i2c0 = &amp;i2c1;</span><br><span class="line">		i2c1 = &amp;i2c2;</span><br><span class="line">		i2c2 = &amp;i2c3;</span><br><span class="line">		i2c3 = &amp;i2c4;</span><br><span class="line">        i2c4 = &amp;i2c5; <span class="comment">// 这行是新添加的</span></span><br><span class="line">		mmc0 = &amp;usdhc1;</span><br><span class="line">		<span class="comment">//......</span></span><br><span class="line">	&#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p><strong>为什么需要修改aliases呢？</strong>因为在添加添加adapter时，会通过<code>aliases</code>的别名编号配置<code>adapter-&gt;nr</code>总线编号。注册成功后，会创建<code>/dev/i2c-4</code>设备。在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1352">i2c_add_adapter()</a>函数中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">i2c_add_adapter</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adapter)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">device</span> *<span class="title">dev</span> =</span> &amp;adapter-&gt;dev;</span><br><span class="line">	<span class="type">int</span> id;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (dev-&gt;of_node) &#123;</span><br><span class="line">		id = of_alias_get_id(dev-&gt;of_node, <span class="string">&quot;i2c&quot;</span>);</span><br><span class="line">		<span class="keyword">if</span> (id &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">			adapter-&gt;nr = id;</span><br><span class="line">			<span class="keyword">return</span> __i2c_add_numbered_adapter(adapter);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	adapter-&gt;nr = id;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> i2c_register_adapter(adapter);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这样在注册<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L670">i2c_adapter</a>的时候<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1352">i2c_add_adapter()</a>函数就会获取到对应的一个编号。</p>
<h4 id="4-3-3-open-drain属性"><a href="#4-3-3-open-drain属性" class="headerlink" title="4.3.3 open drain属性"></a><font size=3>4.3.3 open drain属性</font></h4><p>使用GPIO模拟I2C模式时，一般GPIO需要工作在开漏模式。在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c#L203">of_i2c_gpio_get_props()</a>函数中，解析是否有定义open drain相关属性。如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">of_i2c_gpio_get_props</span><span class="params">(<span class="keyword">struct</span> device_node *np,</span></span><br><span class="line"><span class="params">				  <span class="keyword">struct</span> i2c_gpio_platform_data *pdata)</span></span><br><span class="line">&#123;</span><br><span class="line">	u32 reg;</span><br><span class="line"></span><br><span class="line">	of_property_read_u32(np, <span class="string">&quot;i2c-gpio,delay-us&quot;</span>, &amp;pdata-&gt;udelay);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (!of_property_read_u32(np, <span class="string">&quot;i2c-gpio,timeout-ms&quot;</span>, &amp;reg))</span><br><span class="line">		pdata-&gt;timeout = msecs_to_jiffies(reg);</span><br><span class="line"></span><br><span class="line">	pdata-&gt;sda_is_open_drain =</span><br><span class="line">		of_property_read_bool(np, <span class="string">&quot;i2c-gpio,sda-open-drain&quot;</span>);</span><br><span class="line">	pdata-&gt;scl_is_open_drain =</span><br><span class="line">		of_property_read_bool(np, <span class="string">&quot;i2c-gpio,scl-open-drain&quot;</span>);</span><br><span class="line">	pdata-&gt;scl_is_output_only =</span><br><span class="line">		of_property_read_bool(np, <span class="string">&quot;i2c-gpio,scl-output-only&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当定义i2c-gpio,sda-open-drain和i2c-gpio,scl-open-drain属性后，说明是其它子系统已经将该GPIO配置成开漏输出了，这里不再进行开漏的配置。如果dts里面不定义，就启动<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/gpio/consumer.h#L45">GPIOD_OUT_HIGH_OPEN_DRAIN</a>配置GPIO，我们可以看一下<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c#L295">i2c_gpio_probe()</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (pdata-&gt;sda_is_open_drain)</span><br><span class="line">	gflags = GPIOD_OUT_HIGH;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">	gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;</span><br><span class="line"><span class="comment">//......</span></span><br><span class="line"><span class="keyword">if</span> (pdata-&gt;scl_is_open_drain)</span><br><span class="line">	gflags = GPIOD_OUT_HIGH;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">	gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;</span><br></pre></td></tr></table></figure>

<p>所以，这里可以不定义该属性，它将会在函数<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c#L295">i2c_gpio_probe()</a>中配置为开漏。</p>
<h4 id="4-3-4-添加i2c设备"><a href="#4-3-4-添加i2c设备" class="headerlink" title="4.3.4 添加i2c设备"></a><font size=3>4.3.4 添加i2c设备</font></h4><p>有了i2c适配器，就可以添加对应的i2c设备了，这里就和前面一样了。为了方便后面测试模拟I2C总线，这里还是需要一个设备：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&amp;i2c5 &#123;</span><br><span class="line">	status = <span class="string">&quot;okay&quot;</span>;</span><br><span class="line">	</span><br><span class="line">	ap3216c@<span class="number">1</span>e &#123;</span><br><span class="line">		compatible = <span class="string">&quot;alpha,ap3216c&quot;</span>;</span><br><span class="line">		reg = &lt;<span class="number">0x1e</span>&gt;;</span><br><span class="line">	&#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>不过，我们并没有实际的设备挂载在上面，后面可以直接用下面的命令创建模拟的i2c设备：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// 创建一个i2c_client, .name = &quot;eeprom&quot;, .addr=0x50, .adapter是i2c-4</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="built_in">echo</span> eeprom 0x50 &gt; /sys/bus/i2c/devices/i2c-4/new_device</span></span><br><span class="line"></span><br><span class="line">// 删除一个i2c_client</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="built_in">echo</span> 0x50 &gt; /sys/bus/i2c/devices/i2c-4/delete_device</span></span><br></pre></td></tr></table></figure>

<h4 id="4-3-5-开发板测试"><a href="#4-3-5-开发板测试" class="headerlink" title="4.3.5 开发板测试"></a><font size=3>4.3.5 开发板测试</font></h4><p>我们更新开发板的设备树，就会发现这里新增了<code>/dev/i2c-4</code>总线设备，它就是我们新增的GPIO模拟I2C总线设备。</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330170557596.png" alt="image-20250330170557596" />

<p>使用<code>i2c_tools</code>测试该总线，可以正常的识别到设备，说明移植已经成功了。但是我这里实际上并没有接入i2c设备，就算手动创建一个i2c设备，也只会创建对应的设备，但是i2cdetect不会有反应，因为我们实际并没有对应的i2c设备存在，这就会导致i2c向这个地址发数据的时候，并不会收到回应，所以就探测不出来了：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/LV08-01-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F-04-I2C%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90/img/image-20250330170813059.png" alt="image-20250330170813059" />

<h2 id="5-i2c-gpio驱动分析"><a href="#5-i2c-gpio驱动分析" class="headerlink" title="5. i2c-gpio驱动分析"></a><font size=3>5. i2c-gpio驱动分析</font></h2><p>前面知道，GPIO模拟I2C协议的驱动位于 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses">busses - drivers&#x2F;i2c&#x2F;busses</a> 目录。驱动名称为“i2c-gpio”，驱动文件为<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c">i2c-gpio.c - drivers&#x2F;i2c&#x2F;busses&#x2F;i2c-gpio.c</a>，是一个平台设备驱动，它的平台设备驱动结构体为 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c#L399">i2c_gpio_driver</a></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">platform_driver</span> <span class="title">i2c_gpio_driver</span> =</span> &#123;</span><br><span class="line">	.driver		= &#123;</span><br><span class="line">		.name	= <span class="string">&quot;i2c-gpio&quot;</span>,</span><br><span class="line">		.of_match_table	= of_match_ptr(i2c_gpio_dt_ids),</span><br><span class="line">	&#125;,</span><br><span class="line">	.probe		= i2c_gpio_probe,</span><br><span class="line">	.remove		= i2c_gpio_remove,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h3 id="5-1-i2c-gpio-driver-driver-of-match-table"><a href="#5-1-i2c-gpio-driver-driver-of-match-table" class="headerlink" title="5.1 i2c_gpio_driver.driver.of_match_table"></a><font size=3>5.1 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c#L402">i2c_gpio_driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/platform_device.h#L186">driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/device.h#L287">of_match_table</a></font></h3><p>先来看一下这个匹配表，对应的数组为 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c#L391">i2c_gpio_dt_ids</a></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">of_device_id</span> <span class="title">i2c_gpio_dt_ids</span>[] =</span> &#123;</span><br><span class="line">	&#123; .compatible = <span class="string">&quot;i2c-gpio&quot;</span>, &#125;,</span><br><span class="line">	&#123; <span class="comment">/* sentinel */</span> &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);</span><br></pre></td></tr></table></figure>

<p>所以我们前面定义的模拟I2C的compatible属性名称就包含的有”i2c-gpio”。</p>
<h3 id="5-2-i2c-gpio-driver-probe"><a href="#5-2-i2c-gpio-driver-probe" class="headerlink" title="5.2 i2c_gpio_driver.probe"></a><font size=3>5.2 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c#L404">i2c_gpio_driver</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/platform_device.h#L181">probe</a></font></h3><p>当i2c适配器设备和驱动匹配的时候，就会执行.probe函数，在这里就是<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c#L257">i2c_gpio_probe()</a></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">i2c_gpio_probe</span><span class="params">(<span class="keyword">struct</span> platform_device *pdev)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">    <span class="comment">// (1)解析设备树</span></span><br><span class="line">	<span class="keyword">if</span> (np) &#123;</span><br><span class="line">		of_i2c_gpio_get_props(np, pdata);</span><br><span class="line">	&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//......</span></span><br><span class="line">    <span class="comment">// (2)解析dts设备树文件里面定义gpios配置：gpios = &lt;&amp;gpio1 29 GPIO_ACTIVE_HIGH&gt;, &lt;&amp;gpio1 28 GPIO_ACTIVE_HIGH&gt;;</span></span><br><span class="line">    <span class="keyword">if</span> (pdata-&gt;sda_is_open_drain)</span><br><span class="line">		gflags = GPIOD_OUT_HIGH;</span><br><span class="line">	<span class="keyword">else</span></span><br><span class="line">		gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;</span><br><span class="line">	priv-&gt;sda = i2c_gpio_get_desc(dev, <span class="string">&quot;sda&quot;</span>, <span class="number">0</span>, gflags);</span><br><span class="line">	<span class="keyword">if</span> (IS_ERR(priv-&gt;sda))</span><br><span class="line">		<span class="keyword">return</span> PTR_ERR(priv-&gt;sda);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (pdata-&gt;scl_is_open_drain)</span><br><span class="line">		gflags = GPIOD_OUT_HIGH;</span><br><span class="line">	<span class="keyword">else</span></span><br><span class="line">		gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;</span><br><span class="line">	priv-&gt;scl = i2c_gpio_get_desc(dev, <span class="string">&quot;scl&quot;</span>, <span class="number">1</span>, gflags);</span><br><span class="line">	<span class="keyword">if</span> (IS_ERR(priv-&gt;scl))</span><br><span class="line">		<span class="keyword">return</span> PTR_ERR(priv-&gt;scl);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">    <span class="comment">// (3)配置操作SDA和SCL 2个GPIO的函数接口，后面可以通过它设置和获取GPIO的高低电平</span></span><br><span class="line">	bit_data-&gt;setsda = i2c_gpio_setsda_val;</span><br><span class="line">	bit_data-&gt;setscl = i2c_gpio_setscl_val;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (!pdata-&gt;scl_is_output_only)</span><br><span class="line">		bit_data-&gt;getscl = i2c_gpio_getscl;</span><br><span class="line">	bit_data-&gt;getsda = i2c_gpio_getsda;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">    <span class="comment">// (4)注册到i2c-algo-bit.c</span></span><br><span class="line">	adap-&gt;algo_data = bit_data;</span><br><span class="line">	adap-&gt;<span class="class"><span class="keyword">class</span> =</span> I2C_CLASS_HWMON | I2C_CLASS_SPD;</span><br><span class="line">	adap-&gt;dev.parent = dev;</span><br><span class="line">	adap-&gt;dev.of_node = np;</span><br><span class="line"></span><br><span class="line">	adap-&gt;nr = pdev-&gt;id;</span><br><span class="line">	ret = i2c_bit_add_numbered_bus(adap);</span><br><span class="line">	<span class="keyword">if</span> (ret)</span><br><span class="line">		<span class="keyword">return</span> ret;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="5-2-1-解析设备树"><a href="#5-2-1-解析设备树" class="headerlink" title="5.2.1 解析设备树"></a><font size=3>5.2.1 解析设备树</font></h4><p>我们看一下这个 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/busses/i2c-gpio.c#L203">of_i2c_gpio_get_props()</a></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">of_i2c_gpio_get_props</span><span class="params">(<span class="keyword">struct</span> device_node *np,</span></span><br><span class="line"><span class="params">				  <span class="keyword">struct</span> i2c_gpio_platform_data *pdata)</span></span><br><span class="line">&#123;</span><br><span class="line">	u32 reg;</span><br><span class="line"></span><br><span class="line">	of_property_read_u32(np, <span class="string">&quot;i2c-gpio,delay-us&quot;</span>, &amp;pdata-&gt;udelay);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (!of_property_read_u32(np, <span class="string">&quot;i2c-gpio,timeout-ms&quot;</span>, &amp;reg))</span><br><span class="line">		pdata-&gt;timeout = msecs_to_jiffies(reg);</span><br><span class="line"></span><br><span class="line">	pdata-&gt;sda_is_open_drain =</span><br><span class="line">		of_property_read_bool(np, <span class="string">&quot;i2c-gpio,sda-open-drain&quot;</span>);</span><br><span class="line">	pdata-&gt;scl_is_open_drain =</span><br><span class="line">		of_property_read_bool(np, <span class="string">&quot;i2c-gpio,scl-open-drain&quot;</span>);</span><br><span class="line">	pdata-&gt;scl_is_output_only =</span><br><span class="line">		of_property_read_bool(np, <span class="string">&quot;i2c-gpio,scl-output-only&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>i2c-gpio,delay-us：配置每个bit的使用时间，也就是I2C通信时Clock的频率。</li>
<li>i2c-gpio,timeout-ms：配置i2c通信时的超时时间，如果超过这个时间没有收到ack，说明通信失败。</li>
<li>i2c-gpio,sda-open-drain：是否有在其它子系统里面定义了sda gpio为开漏模式，如果有就定义该属性。</li>
<li>i2c-gpio,scl-open-drain：是否有在其它子系统里面定义了scl gpio为开漏模式，如果有就定义该属性。</li>
<li>i2c-gpio,scl-output-only：配置scl gpio只支持输出模式，不支持输入模式。</li>
</ul>
<h4 id="5-2-2-注册到i2c-algo-bit-c"><a href="#5-2-2-注册到i2c-algo-bit-c" class="headerlink" title="5.2.2 注册到i2c-algo-bit.c"></a><font size=3>5.2.2 注册到i2c-algo-bit.c</font></h4><p>我们看一下<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L680">i2c_bit_add_numbered_bus()</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">i2c_bit_add_numbered_bus</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adap)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">return</span> __i2c_bit_add_bus(adap, i2c_add_numbered_adapter);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>它调用的是<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L639">__i2c_bit_add_busr()</a>函数：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> __i2c_bit_add_bus(<span class="keyword">struct</span> i2c_adapter *adap,</span><br><span class="line">			     <span class="type">int</span> (*add_adapter)(<span class="keyword">struct</span> i2c_adapter *))</span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">/* register new adapter to i2c module... */</span></span><br><span class="line">	adap-&gt;algo = &amp;i2c_bit_algo;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line"></span><br><span class="line">	ret = add_adapter(adap);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到这里为i2c-gpio添加了对应的 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/i2c.h#L497">i2c_algorithm</a> ，这里对应的是 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L626">i2c_bit_algo</a>，它的master_xfer成员指向的函数就为模拟i2c添加用于产生i2c访问周期需要的start stop ack信号操作函数。就算我们自己写GPIO模拟I2C驱动，也都必须实现这些函数。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">i2c_algorithm</span> <span class="title">i2c_bit_algo</span> =</span> &#123;</span><br><span class="line">	.master_xfer	= bit_xfer,</span><br><span class="line">	.functionality	= bit_func,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>这个我们后面再看。然后就是调用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c#L1203">add_adapter()</a> 向I2C Core注册一个adapter，注册成功后，<code>&quot;i2c-dev&quot;</code>驱动就会自动创建对应的<code>&quot;/dev/i2c-x&quot;</code>字符设备，然后我们就可以在应用层和驱动层操作该总线。</p>
<h3 id="5-3-i2c-bit-algo"><a href="#5-3-i2c-bit-algo" class="headerlink" title="5.3 i2c_bit_algo"></a><font size=3>5.3 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L626">i2c_bit_algo</a></font></h3><h4 id="5-3-1-i2c-bit-algo-functionality"><a href="#5-3-1-i2c-bit-algo-functionality" class="headerlink" title="5.3.1 i2c_bit_algo.functionality"></a><font size=3>5.3.1 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L626">i2c_bit_algo</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L628">functionality</a></font></h4><p> <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L626">i2c_bit_algo</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L628">functionality</a>对应的函数是<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L615">bit_func()</a></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> u32 <span class="title function_">bit_func</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *adap)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">return</span> I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |</span><br><span class="line">	       I2C_FUNC_SMBUS_READ_BLOCK_DATA |</span><br><span class="line">	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL |</span><br><span class="line">	       I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里就是返回支持的I2C协议。</p>
<h4 id="5-3-2-i2c-bit-algo-master-xfer"><a href="#5-3-2-i2c-bit-algo-master-xfer" class="headerlink" title="5.3.2 i2c_bit_algo.master_xfer"></a><font size=3>5.3.2 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L626">i2c_bit_algo</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L627">master_xfer</a></font></h4><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L626">i2c_bit_algo</a>.<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L627">master_xfer</a>对应的是<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/algos/i2c-algo-bit.c#L540">bit_xfer()</a>函数，这个函数就是为i2c的访问产生i2c访问周期需要的start stop ack信号：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">bit_xfer</span><span class="params">(<span class="keyword">struct</span> i2c_adapter *i2c_adap,</span></span><br><span class="line"><span class="params">		    <span class="keyword">struct</span> i2c_msg msgs[], <span class="type">int</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	i2c_start(adap);</span><br><span class="line">	<span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; num; i++) &#123;</span><br><span class="line">		<span class="comment">//......</span></span><br><span class="line">		<span class="keyword">if</span> (pmsg-&gt;flags &amp; I2C_M_RD) &#123;</span><br><span class="line">			<span class="comment">/* read bytes into buffer*/</span></span><br><span class="line">			ret = readbytes(i2c_adap, pmsg);</span><br><span class="line">			<span class="comment">//......</span></span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			<span class="comment">/* write bytes from buffer */</span></span><br><span class="line">			ret = sendbytes(i2c_adap, pmsg);</span><br><span class="line">			<span class="comment">//......</span></span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	ret = i;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	i2c_stop(adap);</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当我们使用<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/drivers/i2c/i2c-core-base.c#L1913">i2c_transfer()</a> 函数收发数据的时候，就会调用到这个函数。 </p>
<h2 id="6-GPIO模拟I2Cdemo"><a href="#6-GPIO模拟I2Cdemo" class="headerlink" title="6. GPIO模拟I2Cdemo"></a><font size=3>6. GPIO模拟I2Cdemo</font></h2><h3 id="6-1-demo源码"><a href="#6-1-demo源码" class="headerlink" title="6.1 demo源码"></a><font size=3>6.1 demo源码</font></h3><p>这个demo源码可以看这里：<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/blob/master/30_i2c_subsystem/04_i2c_gpio/device_tree/imx6ull-alpha-emmc/imx6ull-alpha-emmc.dtsi">30_i2c_subsystem&#x2F;04_i2c_gpio&#x2F;device_tree&#x2F;imx6ull-alpha-emmc&#x2F;imx6ull-alpha-emmc.dtsi · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a></p>
<p>这个demo主要是修改设备树，驱动是不需要的，另外还修改了<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/blob/master/kernel/dts_common/imx6ul.dtsi">kernel&#x2F;dts_common&#x2F;imx6ul.dtsi · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a></p>
<h3 id="6-2-开发板测试"><a href="#6-2-开发板测试" class="headerlink" title="6.2 开发板测试"></a><font size=3>6.2 开发板测试</font></h3><p>就和这一节的4.3的测试现象一样。</p>
<blockquote>
<p>参考资料：</p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/lcw/p/3297889.html">【驱动】linux下I2C驱动架构全面分析 - Leo.cheng - 博客园</a></p>
<p><a target="_blank" rel="noopener" href="https://www.linuxidc.com/Linux/2017-03/142198.htm">Linux i2c子系统(一) _动手写一个i2c设备驱动_Linux编程_Linux公社-Linux系统门户网站</a></p>
<p><a target="_blank" rel="noopener" href="https://doc.embedfire.com/linux/imx6/driver/zh/latest/linux_driver/i2c_mpu6050.html">I2C子系统–mpu6050驱动实验 野火嵌入式Linux驱动开发实战指南——基于i.MX6ULL系列 文档</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/ZHONGCAI0901/article/details/131167968">【I2C】Linux使用GPIO模拟I2C_i2c dts 配置-CSDN博客</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/landishu/article/details/118441943">Linux内核驱动：gpio模拟i2c驱动_i2c-gpio-CSDN博客</a></p>
</blockquote>

    </div>

    
    
    

    <footer class="post-footer">




    <div>
        
            <div style="text-align:center;color: #ccc;font-size:14px;">
            ----------本文结束
            <i class="fas fa-fan fa-spin" style="color: #FF1493; font-size: 1rem"></i>
            感谢您的阅读----------
            </div>
        
    </div>





  
  <div class="my_post_copyright"> 
    <p><span>文章标题:</span><a href="/post/2d9f95cb.html">LV08-01-I2C子系统-04-I2C驱动框架分析</a></p>
    <p><span>文章作者:</span><a href="/" title="欢迎访问 《苏木》 的学习笔记">苏木</a></p>
    <p><span>发布时间:</span>2025年04月08日 - 07:38</p>
    <p><span>最后更新:</span>2025年06月14日 - 00:25</p>
    <p><span>原始链接:</span><a href="/post/2d9f95cb.html" title="LV08-01-I2C子系统-04-I2C驱动框架分析">https://sumumm.github.io/post/2d9f95cb.html</a></p>
    <p><span>许可协议:</span><i class="fab fa-creative-commons"></i> <a rel="license" href= "https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>  
  </div>
  


          <div class="post-tags">
              <a href="/tags/LV08-I2C%E5%AD%90%E7%B3%BB%E7%BB%9F/" rel="tag"><i class="fa fa-tag"></i> LV08-I2C子系统</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/post/55b3c723.html" rel="prev" title="LV08-I2C子系统-readme">
                  <i class="fa fa-angle-left"></i> LV08-I2C子系统-readme
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/post/1c16e473.html" rel="next" title="LV08-01-I2C子系统-03-I2C驱动框架简介">
                  LV08-01-I2C子系统-03-I2C驱动框架简介 <i class="fa fa-angle-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">

  <div class="copyright">
    &copy; 2017 – 
    <span itemprop="copyrightYear">2025</span>
    <span class="with-love">
      <i class="fa fa-heart"></i>
    </span>
    <span class="author" itemprop="copyrightHolder">苏木</span>
  </div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">3.7m</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">225:26</span>
  </span>
</div>




    <span id="sitetime"></span>
    <script defer language=javascript>
        function siteTime()
        {
            window.setTimeout("siteTime()", 1000);
            var seconds = 1000;
            var minutes = seconds * 60;
            var hours = minutes * 60;
            var days = hours * 24;
            var years = days * 365;
            var today = new Date();
            var todayYear = today.getFullYear();
            var todayMonth = today.getMonth()+1;
            var todayDate = today.getDate();
            var todayHour = today.getHours();
            var todayMinute = today.getMinutes();
            var todaySecond = today.getSeconds();
            /*==================================================
            Date.UTC() -- 返回date对象距世界标准时间(UTC)1970年1月1日午夜之间的毫秒数(时间戳)
            year        - 作为date对象的年份，为4位年份值
            month       - 0-11之间的整数，做为date对象的月份
            day         - 1-31之间的整数，做为date对象的天数
            hours       - 0(午夜24点)-23之间的整数，做为date对象的小时数
            minutes     - 0-59之间的整数，做为date对象的分钟数
            seconds     - 0-59之间的整数，做为date对象的秒数
            microseconds - 0-999之间的整数，做为date对象的毫秒数
            ==================================================*/
            var t1 = Date.UTC(2017, 
                              5, 
                              19, 
                              0, 
                              0, 
                              0); //北京时间
            var t2 = Date.UTC(todayYear,todayMonth,todayDate,todayHour,todayMinute,todaySecond);
            var diff = t2-t1;
            var diffYears = Math.floor(diff/years);
            var diffDays = Math.floor((diff/days)-diffYears*365);
            var diffHours = Math.floor((diff-(diffYears*365+diffDays)*days)/hours);
            var diffMinutes = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours)/minutes);
            var diffSeconds = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours-diffMinutes*minutes)/seconds);
            document.getElementById("sitetime").innerHTML="已在这里 "+diffYears+" 年 "+diffDays+" 天 "+diffHours+" 小时 "+diffMinutes+" 分钟 "+diffSeconds+" 秒";
        }
        siteTime();
    </script>



    </div>
  </footer>

  
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/next-theme-pjax/0.6.0/pjax.min.js" integrity="sha256-vxLn1tSKWD4dqbMRyv940UYw4sXgMtYcK6reefzZrao=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.umd.js" integrity="sha256-ytMJGN3toR+a84u7g7NuHm91VIR06Q41kMWDr2pq7Zo=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.16.0/lozad.min.js" integrity="sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script><script src="/js/pjax.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>




  <script src="/js/third-party/fancybox.js"></script>

  <script src="/js/third-party/pace.js"></script>


  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>


 
        <div id="click-show-text"
            data-mobile = false
            data-text = 富强,民主,文明,和谐,自由,平等,公正,法制,爱国,敬业,诚信,友善
            data-fontsize = 15px
            data-random= false>
        </div>
       

      
        <script async src=https://cdn.jsdelivr.net/npm/hexo-next-mouse-effect@latest/click/showText.js></script>
      

      
    




    <script async src="/js/fancybox_param.js"></script>





<!-- APlayer本体 -->



</body>
</html>
